xref: /onnv-gate/usr/src/uts/common/io/ipw/ipw2100.c (revision 11878:ac93462db6d7)
13847Seh146360 /*
2*11878SVenu.Iyer@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
33847Seh146360  * Use is subject to license terms.
43847Seh146360  */
53847Seh146360 
63847Seh146360 /*
73847Seh146360  * Copyright(c) 2004
83847Seh146360  *	Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
93847Seh146360  *
103847Seh146360  * Redistribution and use in source and binary forms, with or without
113847Seh146360  * modification, are permitted provided that the following conditions
123847Seh146360  * are met:
133847Seh146360  * 1. Redistributions of source code must retain the above copyright
143847Seh146360  *    notice unmodified, this list of conditions, and the following
153847Seh146360  *    disclaimer.
163847Seh146360  * 2. Redistributions in binary form must reproduce the above copyright
173847Seh146360  *    notice, this list of conditions and the following disclaimer in the
183847Seh146360  *    documentation and/or other materials provided with the distribution.
193847Seh146360  *
203847Seh146360  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
213847Seh146360  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
223847Seh146360  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
233847Seh146360  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
243847Seh146360  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
253847Seh146360  * DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
263847Seh146360  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
273847Seh146360  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
283847Seh146360  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
293847Seh146360  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
303847Seh146360  * SUCH DAMAGE.
313847Seh146360  */
323847Seh146360 
333847Seh146360 #include <sys/types.h>
343847Seh146360 #include <sys/byteorder.h>
353847Seh146360 #include <sys/conf.h>
363847Seh146360 #include <sys/cmn_err.h>
373847Seh146360 #include <sys/stat.h>
383847Seh146360 #include <sys/ddi.h>
393847Seh146360 #include <sys/sunddi.h>
403847Seh146360 #include <sys/strsubr.h>
413847Seh146360 #include <sys/ethernet.h>
423847Seh146360 #include <inet/common.h>
433847Seh146360 #include <inet/nd.h>
443847Seh146360 #include <inet/mi.h>
453847Seh146360 #include <sys/note.h>
463847Seh146360 #include <sys/stream.h>
473847Seh146360 #include <sys/strsun.h>
483847Seh146360 #include <sys/modctl.h>
493847Seh146360 #include <sys/devops.h>
503847Seh146360 #include <sys/dlpi.h>
518275SEric Cheng #include <sys/mac_provider.h>
525903Ssowmini #include <net/if.h>
533847Seh146360 #include <sys/mac_wifi.h>
543847Seh146360 #include <sys/varargs.h>
553847Seh146360 #include <sys/policy.h>
563847Seh146360 
573847Seh146360 #include "ipw2100.h"
583847Seh146360 #include "ipw2100_impl.h"
593847Seh146360 #include <inet/wifi_ioctl.h>
603847Seh146360 
613847Seh146360 /*
623847Seh146360  * kCF framework include files
633847Seh146360  */
643847Seh146360 #include <sys/crypto/common.h>
653847Seh146360 #include <sys/crypto/api.h>
663847Seh146360 
673847Seh146360 static void   *ipw2100_ssp	= NULL;
687656SSherry.Moore@Sun.COM static char   ipw2100_ident[]	= IPW2100_DRV_DESC;
693847Seh146360 
703847Seh146360 /*
713847Seh146360  * PIO access attribute for register
723847Seh146360  */
733847Seh146360 static ddi_device_acc_attr_t ipw2100_csr_accattr = {
743847Seh146360 	DDI_DEVICE_ATTR_V0,
753847Seh146360 	DDI_STRUCTURE_LE_ACC,
763847Seh146360 	DDI_STRICTORDER_ACC
773847Seh146360 };
783847Seh146360 
793847Seh146360 static ddi_device_acc_attr_t ipw2100_dma_accattr = {
803847Seh146360 	DDI_DEVICE_ATTR_V0,
813847Seh146360 	DDI_NEVERSWAP_ACC,
823847Seh146360 	DDI_STRICTORDER_ACC
833847Seh146360 };
843847Seh146360 
853847Seh146360 static ddi_dma_attr_t ipw2100_dma_attr = {
863847Seh146360 	DMA_ATTR_V0,
873847Seh146360 	0x0000000000000000ULL,
883847Seh146360 	0x00000000ffffffffULL,
893847Seh146360 	0x00000000ffffffffULL,
903847Seh146360 	0x0000000000000004ULL,
913847Seh146360 	0xfff,
923847Seh146360 	1,
933847Seh146360 	0x00000000ffffffffULL,
943847Seh146360 	0x00000000ffffffffULL,
953847Seh146360 	1,
963847Seh146360 	1,
973847Seh146360 	0
983847Seh146360 };
993847Seh146360 
1003847Seh146360 static const struct ieee80211_rateset ipw2100_rateset_11b = { 4,
1013847Seh146360 	{2, 4, 11, 22}
1023847Seh146360 };
1033847Seh146360 
1043847Seh146360 /*
1053847Seh146360  * For mfthread only
1063847Seh146360  */
1073847Seh146360 extern pri_t minclsyspri;
1083847Seh146360 
1093847Seh146360 /*
1103847Seh146360  * ipw2100 specific hardware operations
1113847Seh146360  */
1123847Seh146360 static void	ipw2100_hwconf_get(struct ipw2100_softc *sc);
1133847Seh146360 static int	ipw2100_chip_reset(struct ipw2100_softc *sc);
1143847Seh146360 static void	ipw2100_master_stop(struct ipw2100_softc *sc);
1153847Seh146360 static void	ipw2100_stop(struct ipw2100_softc *sc);
1163847Seh146360 static int	ipw2100_config(struct ipw2100_softc *sc);
1173847Seh146360 static int	ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type,
1183847Seh146360     void *buf, size_t len);
1193847Seh146360 static int	ipw2100_dma_region_alloc(struct ipw2100_softc *sc,
1203847Seh146360     struct dma_region *dr, size_t size, uint_t dir, uint_t flags);
1213847Seh146360 static void	ipw2100_dma_region_free(struct dma_region *dr);
1223847Seh146360 static void	ipw2100_tables_init(struct ipw2100_softc *sc);
1233847Seh146360 static void	ipw2100_ring_hwsetup(struct ipw2100_softc *sc);
1243847Seh146360 static int	ipw2100_ring_alloc(struct ipw2100_softc *sc);
1253847Seh146360 static void	ipw2100_ring_free(struct ipw2100_softc *sc);
1263847Seh146360 static void	ipw2100_ring_reset(struct ipw2100_softc *sc);
1273847Seh146360 static int	ipw2100_ring_init(struct ipw2100_softc *sc);
1283847Seh146360 
1293847Seh146360 /*
1303847Seh146360  * GLD specific operations
1313847Seh146360  */
1323847Seh146360 static int	ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val);
1333847Seh146360 static int	ipw2100_m_start(void *arg);
1343847Seh146360 static void	ipw2100_m_stop(void *arg);
1353847Seh146360 static int	ipw2100_m_unicst(void *arg, const uint8_t *macaddr);
1363847Seh146360 static int	ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *m);
1373847Seh146360 static int	ipw2100_m_promisc(void *arg, boolean_t on);
1383847Seh146360 static mblk_t  *ipw2100_m_tx(void *arg, mblk_t *mp);
1393847Seh146360 static void	ipw2100_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
1408367SFei.Feng@Sun.COM static int	ipw2100_m_setprop(void *arg, const char *pr_name,
1418367SFei.Feng@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
1428367SFei.Feng@Sun.COM static int	ipw2100_m_getprop(void *arg, const char *pr_name,
143*11878SVenu.Iyer@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
144*11878SVenu.Iyer@Sun.COM static void	ipw2100_m_propinfo(void *, const char *, mac_prop_id_t,
145*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t);
1463847Seh146360 
1473847Seh146360 /*
1483847Seh146360  * Interrupt and Data transferring operations
1493847Seh146360  */
1503847Seh146360 static uint_t	ipw2100_intr(caddr_t arg);
1513847Seh146360 static int	ipw2100_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type);
1523847Seh146360 static void	ipw2100_rcvpkt(struct ipw2100_softc *sc,
1533847Seh146360     struct ipw2100_status *status, uint8_t *rxbuf);
1543847Seh146360 
1553847Seh146360 /*
1563847Seh146360  * WiFi specific operations
1573847Seh146360  */
1583847Seh146360 static int	ipw2100_newstate(struct ieee80211com *ic,
1593847Seh146360     enum ieee80211_state state, int arg);
1603847Seh146360 static void	ipw2100_thread(struct ipw2100_softc *sc);
1613847Seh146360 
1623847Seh146360 /*
1633847Seh146360  * IOCTL Handler
1643847Seh146360  */
1653847Seh146360 static int	ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m);
1663847Seh146360 static int	ipw2100_getset(struct ipw2100_softc *sc,
1673847Seh146360     mblk_t *m, uint32_t cmd, boolean_t *need_net80211);
1683847Seh146360 static int	ipw_wificfg_radio(struct ipw2100_softc *sc,
1693847Seh146360     uint32_t cmd,  wldp_t *outfp);
1703847Seh146360 static int	ipw_wificfg_desrates(wldp_t *outfp);
1713847Seh146360 static int	ipw_wificfg_disassoc(struct ipw2100_softc *sc,
1723847Seh146360     wldp_t *outfp);
1733847Seh146360 
1743847Seh146360 /*
1758550SSeth.Goldberg@Sun.COM  * Suspend / Resume operations
1768550SSeth.Goldberg@Sun.COM  */
1778550SSeth.Goldberg@Sun.COM static int	ipw2100_cpr_suspend(struct ipw2100_softc *sc);
1788550SSeth.Goldberg@Sun.COM static int	ipw2100_cpr_resume(struct ipw2100_softc *sc);
1798550SSeth.Goldberg@Sun.COM 
1808550SSeth.Goldberg@Sun.COM /*
1813847Seh146360  * Mac Call Back entries
1823847Seh146360  */
1833847Seh146360 mac_callbacks_t	ipw2100_m_callbacks = {
184*11878SVenu.Iyer@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
1853847Seh146360 	ipw2100_m_stat,
1863847Seh146360 	ipw2100_m_start,
1873847Seh146360 	ipw2100_m_stop,
1883847Seh146360 	ipw2100_m_promisc,
1893847Seh146360 	ipw2100_m_multicst,
1903847Seh146360 	ipw2100_m_unicst,
1913847Seh146360 	ipw2100_m_tx,
192*11878SVenu.Iyer@Sun.COM 	NULL,
1938367SFei.Feng@Sun.COM 	ipw2100_m_ioctl,
1948367SFei.Feng@Sun.COM 	NULL,
1958367SFei.Feng@Sun.COM 	NULL,
1968367SFei.Feng@Sun.COM 	NULL,
1978367SFei.Feng@Sun.COM 	ipw2100_m_setprop,
198*11878SVenu.Iyer@Sun.COM 	ipw2100_m_getprop,
199*11878SVenu.Iyer@Sun.COM 	ipw2100_m_propinfo
2003847Seh146360 };
2013847Seh146360 
2023847Seh146360 
2033847Seh146360 /*
2043847Seh146360  * DEBUG Facility
2053847Seh146360  */
2063847Seh146360 #define	MAX_MSG (128)
2073847Seh146360 uint32_t ipw2100_debug = 0;
2083847Seh146360 /*
2093847Seh146360  * supported debug marsks:
2103847Seh146360  *	| IPW2100_DBG_INIT
2113847Seh146360  *	| IPW2100_DBG_GLD
2123847Seh146360  *	| IPW2100_DBG_TABLE
2133847Seh146360  *	| IPW2100_DBG_SOFTINT
2143847Seh146360  *	| IPW2100_DBG_CSR
2153847Seh146360  *	| IPW2100_DBG_INT
2163847Seh146360  *	| IPW2100_DBG_FW
2173847Seh146360  *	| IPW2100_DBG_IOCTL
2183847Seh146360  *	| IPW2100_DBG_HWCAP
2193847Seh146360  *	| IPW2100_DBG_STATISTIC
2203847Seh146360  *	| IPW2100_DBG_RING
2213847Seh146360  *	| IPW2100_DBG_WIFI
2228367SFei.Feng@Sun.COM  *	| IPW2100_DBG_BRUSSELS
2233847Seh146360  */
2243847Seh146360 
2253847Seh146360 /*
2263847Seh146360  * global tuning parameters to work around unknown hardware issues
2273847Seh146360  */
2283847Seh146360 static uint32_t delay_config_stable 	= 100000;	/* 100ms */
2293847Seh146360 static uint32_t delay_fatal_recover	= 100000 * 20;	/* 2s */
2303847Seh146360 static uint32_t delay_aux_thread 	= 100000;	/* 100ms */
2313847Seh146360 
2323847Seh146360 void
ipw2100_dbg(dev_info_t * dip,int level,const char * fmt,...)2333847Seh146360 ipw2100_dbg(dev_info_t *dip, int level, const char *fmt, ...)
2343847Seh146360 {
2353847Seh146360 	va_list	ap;
2363847Seh146360 	char    buf[MAX_MSG];
2373847Seh146360 	int	instance;
2383847Seh146360 
2393847Seh146360 	va_start(ap, fmt);
2403847Seh146360 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
2413847Seh146360 	va_end(ap);
2423847Seh146360 
2433847Seh146360 	if (dip) {
2443847Seh146360 		instance = ddi_get_instance(dip);
2453847Seh146360 		cmn_err(level, "%s%d: %s", IPW2100_DRV_NAME, instance, buf);
2463847Seh146360 	} else
2473847Seh146360 		cmn_err(level, "%s: %s", IPW2100_DRV_NAME, buf);
2483847Seh146360 }
2493847Seh146360 
2503847Seh146360 /*
2513847Seh146360  * device operations
2523847Seh146360  */
2533847Seh146360 int
ipw2100_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2543847Seh146360 ipw2100_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2553847Seh146360 {
2563847Seh146360 	struct ipw2100_softc	*sc;
2573847Seh146360 	ddi_acc_handle_t	cfgh;
2583847Seh146360 	caddr_t			regs;
2593847Seh146360 	struct ieee80211com	*ic;
2603847Seh146360 	int			instance, err, i;
2613847Seh146360 	char			strbuf[32];
2623847Seh146360 	wifi_data_t		wd = { 0 };
2633847Seh146360 	mac_register_t		*macp;
2643847Seh146360 
2658550SSeth.Goldberg@Sun.COM 	switch (cmd) {
2668550SSeth.Goldberg@Sun.COM 	case DDI_ATTACH:
2678550SSeth.Goldberg@Sun.COM 		break;
2688550SSeth.Goldberg@Sun.COM 	case DDI_RESUME:
2698550SSeth.Goldberg@Sun.COM 		sc = ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip));
2708550SSeth.Goldberg@Sun.COM 		if (sc == NULL) {
2718550SSeth.Goldberg@Sun.COM 			err = DDI_FAILURE;
2728550SSeth.Goldberg@Sun.COM 			goto fail1;
2738550SSeth.Goldberg@Sun.COM 		}
2748550SSeth.Goldberg@Sun.COM 		return (ipw2100_cpr_resume(sc));
2758550SSeth.Goldberg@Sun.COM 	default:
2763847Seh146360 		err = DDI_FAILURE;
2773847Seh146360 		goto fail1;
2783847Seh146360 	}
2793847Seh146360 
2803847Seh146360 	instance = ddi_get_instance(dip);
2813847Seh146360 	err = ddi_soft_state_zalloc(ipw2100_ssp, instance);
2823847Seh146360 	if (err != DDI_SUCCESS) {
2833847Seh146360 		IPW2100_WARN((dip, CE_WARN,
2843847Seh146360 		    "ipw2100_attach(): unable to allocate soft state\n"));
2853847Seh146360 		goto fail1;
2863847Seh146360 	}
2873847Seh146360 	sc = ddi_get_soft_state(ipw2100_ssp, instance);
2883847Seh146360 	sc->sc_dip = dip;
2893847Seh146360 
2903847Seh146360 	/*
2913847Seh146360 	 * Map config spaces register
2923847Seh146360 	 */
2933847Seh146360 	err = ddi_regs_map_setup(dip, IPW2100_PCI_CFG_RNUM, &regs,
2943847Seh146360 	    0, 0, &ipw2100_csr_accattr, &cfgh);
2953847Seh146360 	if (err != DDI_SUCCESS) {
2963847Seh146360 		IPW2100_WARN((dip, CE_WARN,
2973847Seh146360 		    "ipw2100_attach(): unable to map spaces regs\n"));
2983847Seh146360 		goto fail2;
2993847Seh146360 	}
3003847Seh146360 	ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0);
3013847Seh146360 	ddi_regs_map_free(&cfgh);
3023847Seh146360 
3033847Seh146360 	/*
3043847Seh146360 	 * Map operating registers
3053847Seh146360 	 */
3063847Seh146360 	err = ddi_regs_map_setup(dip, IPW2100_PCI_CSR_RNUM, &sc->sc_regs,
3073847Seh146360 	    0, 0, &ipw2100_csr_accattr, &sc->sc_ioh);
3083847Seh146360 	if (err != DDI_SUCCESS) {
3093847Seh146360 		IPW2100_WARN((dip, CE_WARN,
3103847Seh146360 		    "ipw2100_attach(): unable to map device regs\n"));
3113847Seh146360 		goto fail2;
3123847Seh146360 	}
3133847Seh146360 
3143847Seh146360 	/*
3153847Seh146360 	 * Reset the chip
3163847Seh146360 	 */
3173847Seh146360 	err = ipw2100_chip_reset(sc);
3183847Seh146360 	if (err != DDI_SUCCESS) {
3193847Seh146360 		IPW2100_WARN((dip, CE_WARN,
3203847Seh146360 		    "ipw2100_attach(): reset failed\n"));
3213847Seh146360 		goto fail3;
3223847Seh146360 	}
3233847Seh146360 
3243847Seh146360 	/*
3253847Seh146360 	 * Get the hw conf, including MAC address, then init all rings.
3263847Seh146360 	 */
3273847Seh146360 	ipw2100_hwconf_get(sc);
3283847Seh146360 	err = ipw2100_ring_init(sc);
3293847Seh146360 	if (err != DDI_SUCCESS) {
3303847Seh146360 		IPW2100_WARN((dip, CE_WARN,
3313847Seh146360 		    "ipw2100_attach(): "
3323847Seh146360 		    "unable to allocate and initialize rings\n"));
3333847Seh146360 		goto fail3;
3343847Seh146360 	}
3353847Seh146360 
3363847Seh146360 	/*
3373847Seh146360 	 * Initialize mutexs and condvars
3383847Seh146360 	 */
3393847Seh146360 	err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk);
3403847Seh146360 	if (err != DDI_SUCCESS) {
3413847Seh146360 		IPW2100_WARN((dip, CE_WARN,
3423847Seh146360 		    "ipw2100_attach(): ddi_get_iblock_cookie() failed\n"));
3433847Seh146360 		goto fail4;
3443847Seh146360 	}
3453847Seh146360 	/*
3463847Seh146360 	 * interrupt lock
3473847Seh146360 	 */
3483847Seh146360 	mutex_init(&sc->sc_ilock, "interrupt-lock", MUTEX_DRIVER,
3493847Seh146360 	    (void *) sc->sc_iblk);
3503847Seh146360 	cv_init(&sc->sc_fw_cond, "firmware", CV_DRIVER, NULL);
3513847Seh146360 	cv_init(&sc->sc_cmd_cond, "command", CV_DRIVER, NULL);
3523847Seh146360 	/*
3533847Seh146360 	 * tx ring lock
3543847Seh146360 	 */
3553847Seh146360 	mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER,
3563847Seh146360 	    (void *) sc->sc_iblk);
3573847Seh146360 	cv_init(&sc->sc_tx_cond, "tx-ring", CV_DRIVER, NULL);
3583847Seh146360 	/*
3593847Seh146360 	 * rescheuled lock
3603847Seh146360 	 */
3613847Seh146360 	mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER,
3623847Seh146360 	    (void *) sc->sc_iblk);
3633847Seh146360 	/*
3643847Seh146360 	 * initialize the mfthread
3653847Seh146360 	 */
3663847Seh146360 	mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER,
3673847Seh146360 	    (void *) sc->sc_iblk);
3683847Seh146360 	cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL);
3693847Seh146360 	sc->sc_mf_thread = NULL;
3703847Seh146360 	sc->sc_mfthread_switch = 0;
3713847Seh146360 	/*
3723847Seh146360 	 * Initialize the wifi part, which will be used by
3733847Seh146360 	 * generic layer
3743847Seh146360 	 */
3753847Seh146360 	ic = &sc->sc_ic;
3763847Seh146360 	ic->ic_phytype  = IEEE80211_T_DS;
3773847Seh146360 	ic->ic_opmode   = IEEE80211_M_STA;
3783847Seh146360 	ic->ic_state    = IEEE80211_S_INIT;
3793847Seh146360 	ic->ic_maxrssi  = 49;
3803847Seh146360 	/*
3813847Seh146360 	 * Future, could use s/w to handle encryption: IEEE80211_C_WEP
3823847Seh146360 	 * and need to add support for IEEE80211_C_IBSS
3833847Seh146360 	 */
3843847Seh146360 	ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT |
3853847Seh146360 	    IEEE80211_C_PMGT;
3863847Seh146360 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2100_rateset_11b;
3873847Seh146360 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr);
3883847Seh146360 	for (i = 1; i < 16; i++) {
3893847Seh146360 		if (sc->sc_chmask &(1 << i)) {
3903847Seh146360 			/* IEEE80211_CHAN_B */
3913847Seh146360 			ic->ic_sup_channels[i].ich_freq  = ieee80211_ieee2mhz(i,
3923847Seh146360 			    IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK);
3933847Seh146360 			ic->ic_sup_channels[i].ich_flags =
3943847Seh146360 			    IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK;
3953847Seh146360 		}
3963847Seh146360 	}
3973847Seh146360 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
3983847Seh146360 	ic->ic_xmit = ipw2100_send;
3993847Seh146360 	/*
4003847Seh146360 	 * init Wifi layer
4013847Seh146360 	 */
4023847Seh146360 	ieee80211_attach(ic);
4033847Seh146360 
4043847Seh146360 	/*
4053847Seh146360 	 * Override 80211 default routines
4063847Seh146360 	 */
4073847Seh146360 	ieee80211_media_init(ic);
4083847Seh146360 	sc->sc_newstate = ic->ic_newstate;
4093847Seh146360 	ic->ic_newstate = ipw2100_newstate;
4103847Seh146360 	/*
4113847Seh146360 	 * initialize default tx key
4123847Seh146360 	 */
4133847Seh146360 	ic->ic_def_txkey = 0;
4143847Seh146360 	/*
4153847Seh146360 	 * Set the Authentication to AUTH_Open only.
4163847Seh146360 	 */
4173847Seh146360 	sc->sc_authmode = IEEE80211_AUTH_OPEN;
4183847Seh146360 
4193847Seh146360 	/*
4203847Seh146360 	 * Add the interrupt handler
4213847Seh146360 	 */
4223847Seh146360 	err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL,
4233847Seh146360 	    ipw2100_intr, (caddr_t)sc);
4243847Seh146360 	if (err != DDI_SUCCESS) {
4253847Seh146360 		IPW2100_WARN((dip, CE_WARN,
4263847Seh146360 		    "ipw2100_attach(): ddi_add_intr() failed\n"));
4273847Seh146360 		goto fail5;
4283847Seh146360 	}
4293847Seh146360 
4303847Seh146360 	/*
4313847Seh146360 	 * Initialize pointer to device specific functions
4323847Seh146360 	 */
4333847Seh146360 	wd.wd_secalloc = WIFI_SEC_NONE;
4343847Seh146360 	wd.wd_opmode = ic->ic_opmode;
4357194Seh146360 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
4363847Seh146360 
4373847Seh146360 	macp = mac_alloc(MAC_VERSION);
4383847Seh146360 	if (err != 0) {
4393847Seh146360 		IPW2100_WARN((dip, CE_WARN,
4403847Seh146360 		    "ipw2100_attach(): mac_alloc() failed\n"));
4413847Seh146360 		goto fail6;
4423847Seh146360 	}
4433847Seh146360 
4443847Seh146360 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
4453847Seh146360 	macp->m_driver		= sc;
4463847Seh146360 	macp->m_dip		= dip;
4473847Seh146360 	macp->m_src_addr	= ic->ic_macaddr;
4483847Seh146360 	macp->m_callbacks	= &ipw2100_m_callbacks;
4493847Seh146360 	macp->m_min_sdu		= 0;
4503847Seh146360 	macp->m_max_sdu		= IEEE80211_MTU;
4513847Seh146360 	macp->m_pdata		= &wd;
4523847Seh146360 	macp->m_pdata_size	= sizeof (wd);
4533847Seh146360 
4543847Seh146360 	/*
4553847Seh146360 	 * Register the macp to mac
4563847Seh146360 	 */
4573847Seh146360 	err = mac_register(macp, &ic->ic_mach);
4583847Seh146360 	mac_free(macp);
4593847Seh146360 	if (err != DDI_SUCCESS) {
4603847Seh146360 		IPW2100_WARN((dip, CE_WARN,
4613847Seh146360 		    "ipw2100_attach(): mac_register() failed\n"));
4623847Seh146360 		goto fail6;
4633847Seh146360 	}
4643847Seh146360 
4653847Seh146360 	/*
4663847Seh146360 	 * Create minor node of type DDI_NT_NET_WIFI
4673847Seh146360 	 */
4683847Seh146360 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
4693847Seh146360 	    IPW2100_DRV_NAME, instance);
4703847Seh146360 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
4713847Seh146360 	    instance + 1, DDI_NT_NET_WIFI, 0);
4723847Seh146360 	if (err != DDI_SUCCESS)
4733847Seh146360 		IPW2100_WARN((dip, CE_WARN,
4743847Seh146360 		    "ipw2100_attach(): ddi_create_minor_node() failed\n"));
4753847Seh146360 
4763847Seh146360 	/*
4773847Seh146360 	 * Cache firmware, always return true
4783847Seh146360 	 */
4793847Seh146360 	(void) ipw2100_cache_firmware(sc);
4803847Seh146360 
4813847Seh146360 	/*
4823847Seh146360 	 * Notify link is down now
4833847Seh146360 	 */
4843847Seh146360 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
4853847Seh146360 
4863847Seh146360 	/*
4873847Seh146360 	 * create the mf thread to handle the link status,
4883847Seh146360 	 * recovery fatal error, etc.
4893847Seh146360 	 */
4903847Seh146360 	sc->sc_mfthread_switch = 1;
4913847Seh146360 	if (sc->sc_mf_thread == NULL)
4923847Seh146360 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
4933847Seh146360 		    ipw2100_thread, sc, 0, &p0, TS_RUN, minclsyspri);
4943847Seh146360 
4953847Seh146360 	return (DDI_SUCCESS);
4963847Seh146360 
4973847Seh146360 fail6:
4983847Seh146360 	ddi_remove_intr(dip, 0, sc->sc_iblk);
4993847Seh146360 fail5:
5003847Seh146360 	ieee80211_detach(ic);
5013847Seh146360 
5023847Seh146360 	mutex_destroy(&sc->sc_ilock);
5033847Seh146360 	mutex_destroy(&sc->sc_tx_lock);
5043847Seh146360 	mutex_destroy(&sc->sc_mflock);
5053847Seh146360 	mutex_destroy(&sc->sc_resched_lock);
5063847Seh146360 	cv_destroy(&sc->sc_mfthread_cv);
5073847Seh146360 	cv_destroy(&sc->sc_tx_cond);
5083847Seh146360 	cv_destroy(&sc->sc_cmd_cond);
5093847Seh146360 	cv_destroy(&sc->sc_fw_cond);
5103847Seh146360 fail4:
5113847Seh146360 	ipw2100_ring_free(sc);
5123847Seh146360 fail3:
5133847Seh146360 	ddi_regs_map_free(&sc->sc_ioh);
5143847Seh146360 fail2:
5153847Seh146360 	ddi_soft_state_free(ipw2100_ssp, instance);
5163847Seh146360 fail1:
5173847Seh146360 	return (err);
5183847Seh146360 }
5193847Seh146360 
5203847Seh146360 int
ipw2100_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5213847Seh146360 ipw2100_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5223847Seh146360 {
5233847Seh146360 	struct ipw2100_softc	*sc =
5243847Seh146360 	    ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip));
5253847Seh146360 	int err;
5263847Seh146360 
5273847Seh146360 	ASSERT(sc != NULL);
5283847Seh146360 
5298550SSeth.Goldberg@Sun.COM 	switch (cmd) {
5308550SSeth.Goldberg@Sun.COM 	case DDI_DETACH:
5318550SSeth.Goldberg@Sun.COM 		break;
5328550SSeth.Goldberg@Sun.COM 	case DDI_SUSPEND:
5338550SSeth.Goldberg@Sun.COM 		return (ipw2100_cpr_suspend(sc));
5348550SSeth.Goldberg@Sun.COM 	default:
5353847Seh146360 		return (DDI_FAILURE);
5368550SSeth.Goldberg@Sun.COM 	}
5373847Seh146360 
5383847Seh146360 	/*
5393847Seh146360 	 * Destroy the mf_thread
5403847Seh146360 	 */
5413847Seh146360 	mutex_enter(&sc->sc_mflock);
5423847Seh146360 	sc->sc_mfthread_switch = 0;
5433847Seh146360 	while (sc->sc_mf_thread != NULL) {
5443847Seh146360 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
5453847Seh146360 			break;
5463847Seh146360 	}
5473847Seh146360 	mutex_exit(&sc->sc_mflock);
5483847Seh146360 
5493847Seh146360 	/*
5508550SSeth.Goldberg@Sun.COM 	 * Unregister from the MAC layer subsystem
5513847Seh146360 	 */
5523847Seh146360 	err = mac_unregister(sc->sc_ic.ic_mach);
5533847Seh146360 	if (err != DDI_SUCCESS)
5543847Seh146360 		return (err);
5553847Seh146360 
5563847Seh146360 	ddi_remove_intr(dip, 0, sc->sc_iblk);
5573847Seh146360 
5583847Seh146360 	/*
5593847Seh146360 	 * destroy the cv
5603847Seh146360 	 */
5613847Seh146360 	mutex_destroy(&sc->sc_ilock);
5623847Seh146360 	mutex_destroy(&sc->sc_tx_lock);
5633847Seh146360 	mutex_destroy(&sc->sc_mflock);
5643847Seh146360 	mutex_destroy(&sc->sc_resched_lock);
5653847Seh146360 	cv_destroy(&sc->sc_mfthread_cv);
5663847Seh146360 	cv_destroy(&sc->sc_tx_cond);
5673847Seh146360 	cv_destroy(&sc->sc_cmd_cond);
5683847Seh146360 	cv_destroy(&sc->sc_fw_cond);
5693847Seh146360 
5703847Seh146360 	/*
5713847Seh146360 	 * detach ieee80211
5723847Seh146360 	 */
5733847Seh146360 	ieee80211_detach(&sc->sc_ic);
5743847Seh146360 
5753847Seh146360 	(void) ipw2100_free_firmware(sc);
5763847Seh146360 	ipw2100_ring_free(sc);
5773847Seh146360 
5783847Seh146360 	ddi_regs_map_free(&sc->sc_ioh);
5793847Seh146360 	ddi_remove_minor_node(dip, NULL);
5803847Seh146360 	ddi_soft_state_free(ipw2100_ssp, ddi_get_instance(dip));
5813847Seh146360 
5823847Seh146360 	return (DDI_SUCCESS);
5833847Seh146360 }
5843847Seh146360 
5858550SSeth.Goldberg@Sun.COM int
ipw2100_cpr_suspend(struct ipw2100_softc * sc)5868550SSeth.Goldberg@Sun.COM ipw2100_cpr_suspend(struct ipw2100_softc *sc)
5878550SSeth.Goldberg@Sun.COM {
5888550SSeth.Goldberg@Sun.COM 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
5898550SSeth.Goldberg@Sun.COM 	    "ipw2100_cpr_suspend(): enter\n"));
5908550SSeth.Goldberg@Sun.COM 
5918550SSeth.Goldberg@Sun.COM 	/*
5928550SSeth.Goldberg@Sun.COM 	 * Destroy the mf_thread
5938550SSeth.Goldberg@Sun.COM 	 */
5948550SSeth.Goldberg@Sun.COM 	mutex_enter(&sc->sc_mflock);
5958550SSeth.Goldberg@Sun.COM 	sc->sc_mfthread_switch = 0;
5968550SSeth.Goldberg@Sun.COM 	while (sc->sc_mf_thread != NULL) {
5978550SSeth.Goldberg@Sun.COM 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
5988550SSeth.Goldberg@Sun.COM 			break;
5998550SSeth.Goldberg@Sun.COM 	}
6008550SSeth.Goldberg@Sun.COM 	mutex_exit(&sc->sc_mflock);
6018550SSeth.Goldberg@Sun.COM 
6028550SSeth.Goldberg@Sun.COM 	/*
6038550SSeth.Goldberg@Sun.COM 	 * stop the hardware; this mask all interrupts
6048550SSeth.Goldberg@Sun.COM 	 */
6058550SSeth.Goldberg@Sun.COM 	ipw2100_stop(sc);
6068550SSeth.Goldberg@Sun.COM 	sc->sc_flags &= ~IPW2100_FLAG_RUNNING;
6078550SSeth.Goldberg@Sun.COM 	sc->sc_suspended = 1;
6088550SSeth.Goldberg@Sun.COM 
6098550SSeth.Goldberg@Sun.COM 	(void) ipw2100_free_firmware(sc);
6108550SSeth.Goldberg@Sun.COM 	ipw2100_ring_free(sc);
6118550SSeth.Goldberg@Sun.COM 
6128550SSeth.Goldberg@Sun.COM 	return (DDI_SUCCESS);
6138550SSeth.Goldberg@Sun.COM }
6148550SSeth.Goldberg@Sun.COM 
6158550SSeth.Goldberg@Sun.COM int
ipw2100_cpr_resume(struct ipw2100_softc * sc)6168550SSeth.Goldberg@Sun.COM ipw2100_cpr_resume(struct ipw2100_softc *sc)
6178550SSeth.Goldberg@Sun.COM {
6188550SSeth.Goldberg@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
6198550SSeth.Goldberg@Sun.COM 	dev_info_t		*dip = sc->sc_dip;
6208550SSeth.Goldberg@Sun.COM 	int			err;
6218550SSeth.Goldberg@Sun.COM 
6228550SSeth.Goldberg@Sun.COM 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
6238550SSeth.Goldberg@Sun.COM 	    "ipw2100_cpr_resume(): enter\n"));
6248550SSeth.Goldberg@Sun.COM 
6258550SSeth.Goldberg@Sun.COM 	/*
6268550SSeth.Goldberg@Sun.COM 	 * Reset the chip
6278550SSeth.Goldberg@Sun.COM 	 */
6288550SSeth.Goldberg@Sun.COM 	err = ipw2100_chip_reset(sc);
6298550SSeth.Goldberg@Sun.COM 	if (err != DDI_SUCCESS) {
6308550SSeth.Goldberg@Sun.COM 		IPW2100_WARN((dip, CE_WARN,
6318550SSeth.Goldberg@Sun.COM 		    "ipw2100_attach(): reset failed\n"));
6328550SSeth.Goldberg@Sun.COM 		return (DDI_FAILURE);
6338550SSeth.Goldberg@Sun.COM 	}
6348550SSeth.Goldberg@Sun.COM 
6358550SSeth.Goldberg@Sun.COM 	/*
6368550SSeth.Goldberg@Sun.COM 	 * Get the hw conf, including MAC address, then init all rings.
6378550SSeth.Goldberg@Sun.COM 	 */
6388550SSeth.Goldberg@Sun.COM 	/* ipw2100_hwconf_get(sc); */
6398550SSeth.Goldberg@Sun.COM 	err = ipw2100_ring_init(sc);
6408550SSeth.Goldberg@Sun.COM 	if (err != DDI_SUCCESS) {
6418550SSeth.Goldberg@Sun.COM 		IPW2100_WARN((dip, CE_WARN,
6428550SSeth.Goldberg@Sun.COM 		    "ipw2100_attach(): "
6438550SSeth.Goldberg@Sun.COM 		    "unable to allocate and initialize rings\n"));
6448550SSeth.Goldberg@Sun.COM 		return (DDI_FAILURE);
6458550SSeth.Goldberg@Sun.COM 	}
6468550SSeth.Goldberg@Sun.COM 
6478550SSeth.Goldberg@Sun.COM 	/*
6488550SSeth.Goldberg@Sun.COM 	 * Cache firmware, always return true
6498550SSeth.Goldberg@Sun.COM 	 */
6508550SSeth.Goldberg@Sun.COM 	(void) ipw2100_cache_firmware(sc);
6518550SSeth.Goldberg@Sun.COM 
6528550SSeth.Goldberg@Sun.COM 	/*
6538550SSeth.Goldberg@Sun.COM 	 * Notify link is down now
6548550SSeth.Goldberg@Sun.COM 	 */
6558550SSeth.Goldberg@Sun.COM 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
6568550SSeth.Goldberg@Sun.COM 
6578550SSeth.Goldberg@Sun.COM 	/*
6588550SSeth.Goldberg@Sun.COM 	 * create the mf thread to handle the link status,
6598550SSeth.Goldberg@Sun.COM 	 * recovery fatal error, etc.
6608550SSeth.Goldberg@Sun.COM 	 */
6618550SSeth.Goldberg@Sun.COM 	sc->sc_mfthread_switch = 1;
6628550SSeth.Goldberg@Sun.COM 	if (sc->sc_mf_thread == NULL)
6638550SSeth.Goldberg@Sun.COM 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
6648550SSeth.Goldberg@Sun.COM 		    ipw2100_thread, sc, 0, &p0, TS_RUN, minclsyspri);
6658550SSeth.Goldberg@Sun.COM 
6668550SSeth.Goldberg@Sun.COM 	/*
6678550SSeth.Goldberg@Sun.COM 	 * enable all interrupts
6688550SSeth.Goldberg@Sun.COM 	 */
6698550SSeth.Goldberg@Sun.COM 	sc->sc_suspended = 0;
6708550SSeth.Goldberg@Sun.COM 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL);
6718550SSeth.Goldberg@Sun.COM 
6728550SSeth.Goldberg@Sun.COM 	/*
6738550SSeth.Goldberg@Sun.COM 	 * initialize ipw2100 hardware
6748550SSeth.Goldberg@Sun.COM 	 */
6758550SSeth.Goldberg@Sun.COM 	(void) ipw2100_init(sc);
6768550SSeth.Goldberg@Sun.COM 
6778550SSeth.Goldberg@Sun.COM 	sc->sc_flags |= IPW2100_FLAG_RUNNING;
6788550SSeth.Goldberg@Sun.COM 
6798550SSeth.Goldberg@Sun.COM 	return (DDI_SUCCESS);
6808550SSeth.Goldberg@Sun.COM }
6818550SSeth.Goldberg@Sun.COM 
6827804SKonstantin.Ananyev@Sun.COM /*
6837804SKonstantin.Ananyev@Sun.COM  * quiesce(9E) entry point.
6847804SKonstantin.Ananyev@Sun.COM  * This function is called when the system is single-threaded at high
6857804SKonstantin.Ananyev@Sun.COM  * PIL with preemption disabled. Therefore, this function must not be
6867804SKonstantin.Ananyev@Sun.COM  * blocked.
6877804SKonstantin.Ananyev@Sun.COM  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
6887804SKonstantin.Ananyev@Sun.COM  * DDI_FAILURE indicates an error condition and should almost never happen.
6897804SKonstantin.Ananyev@Sun.COM  * Contributed by Juergen Keil, <jk@tools.de>.
6907804SKonstantin.Ananyev@Sun.COM  */
6917804SKonstantin.Ananyev@Sun.COM static int
ipw2100_quiesce(dev_info_t * dip)6927804SKonstantin.Ananyev@Sun.COM ipw2100_quiesce(dev_info_t *dip)
6934784Seh146360 {
6944784Seh146360 	struct ipw2100_softc	*sc =
6954784Seh146360 	    ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip));
6967804SKonstantin.Ananyev@Sun.COM 
6977804SKonstantin.Ananyev@Sun.COM 	if (sc == NULL)
6987804SKonstantin.Ananyev@Sun.COM 		return (DDI_FAILURE);
6994784Seh146360 
7007804SKonstantin.Ananyev@Sun.COM 	/*
7017804SKonstantin.Ananyev@Sun.COM 	 * No more blocking is allowed while we are in the
7027804SKonstantin.Ananyev@Sun.COM 	 * quiesce(9E) entry point.
7037804SKonstantin.Ananyev@Sun.COM 	 */
7047804SKonstantin.Ananyev@Sun.COM 	sc->sc_flags |= IPW2100_FLAG_QUIESCED;
7057804SKonstantin.Ananyev@Sun.COM 
7067804SKonstantin.Ananyev@Sun.COM 	/*
7077804SKonstantin.Ananyev@Sun.COM 	 * Disable and mask all interrupts.
7087804SKonstantin.Ananyev@Sun.COM 	 */
7094784Seh146360 	ipw2100_stop(sc);
7104784Seh146360 	return (DDI_SUCCESS);
7114784Seh146360 }
7124784Seh146360 
7133847Seh146360 static void
ipw2100_tables_init(struct ipw2100_softc * sc)7143847Seh146360 ipw2100_tables_init(struct ipw2100_softc *sc)
7153847Seh146360 {
7163847Seh146360 	sc->sc_table1_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE1_BASE);
7173847Seh146360 	sc->sc_table2_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE2_BASE);
7183847Seh146360 }
7193847Seh146360 
7203847Seh146360 static void
ipw2100_stop(struct ipw2100_softc * sc)7213847Seh146360 ipw2100_stop(struct ipw2100_softc *sc)
7223847Seh146360 {
7233847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
7243847Seh146360 
7253847Seh146360 	ipw2100_master_stop(sc);
7263847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_SW_RESET);
7273847Seh146360 	sc->sc_flags &= ~IPW2100_FLAG_FW_INITED;
7283847Seh146360 
7297804SKonstantin.Ananyev@Sun.COM 	if (!(sc->sc_flags & IPW2100_FLAG_QUIESCED))
7307804SKonstantin.Ananyev@Sun.COM 		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
7313847Seh146360 }
7323847Seh146360 
7333847Seh146360 static int
ipw2100_config(struct ipw2100_softc * sc)7343847Seh146360 ipw2100_config(struct ipw2100_softc *sc)
7353847Seh146360 {
7363847Seh146360 	struct ieee80211com		*ic = &sc->sc_ic;
7373847Seh146360 	struct ipw2100_security		sec;
7383847Seh146360 	struct ipw2100_wep_key		wkey;
7393847Seh146360 	struct ipw2100_scan_options	sopt;
7403847Seh146360 	struct ipw2100_configuration	cfg;
7413847Seh146360 	uint32_t			data;
7423847Seh146360 	int				err, i;
7433847Seh146360 
7443847Seh146360 	/*
7453847Seh146360 	 * operation mode
7463847Seh146360 	 */
7473847Seh146360 	switch (ic->ic_opmode) {
7483847Seh146360 	case IEEE80211_M_STA:
7493847Seh146360 	case IEEE80211_M_HOSTAP:
7503847Seh146360 		data = LE_32(IPW2100_MODE_BSS);
7513847Seh146360 		break;
7523847Seh146360 
7533847Seh146360 	case IEEE80211_M_IBSS:
7543847Seh146360 	case IEEE80211_M_AHDEMO:
7553847Seh146360 		data = LE_32(IPW2100_MODE_IBSS);
7563847Seh146360 		break;
7573847Seh146360 	}
7583847Seh146360 
7593847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
7603847Seh146360 	    "ipw2100_config(): Setting mode to %u\n", LE_32(data)));
7613847Seh146360 
7623847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_MODE,
7633847Seh146360 	    &data, sizeof (data));
7643847Seh146360 	if (err != DDI_SUCCESS)
7653847Seh146360 		return (err);
7663847Seh146360 
7673847Seh146360 	/*
7683847Seh146360 	 * operation channel if IBSS or MONITOR
7693847Seh146360 	 */
7703847Seh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
7713847Seh146360 
7723847Seh146360 		data = LE_32(ieee80211_chan2ieee(ic, ic->ic_ibss_chan));
7733847Seh146360 
7743847Seh146360 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
7753847Seh146360 		    "ipw2100_config(): Setting channel to %u\n", LE_32(data)));
7763847Seh146360 
7773847Seh146360 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_CHANNEL,
7783847Seh146360 		    &data, sizeof (data));
7793847Seh146360 		if (err != DDI_SUCCESS)
7803847Seh146360 			return (err);
7813847Seh146360 	}
7823847Seh146360 
7833847Seh146360 	/*
7843847Seh146360 	 * set MAC address
7853847Seh146360 	 */
7863847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
7873847Seh146360 	    "ipw2100_config(): Setting MAC address to "
7883847Seh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
7893847Seh146360 	    ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
7903847Seh146360 	    ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]));
7913847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_MAC_ADDRESS, ic->ic_macaddr,
7923847Seh146360 	    IEEE80211_ADDR_LEN);
7933847Seh146360 	if (err != DDI_SUCCESS)
7943847Seh146360 		return (err);
7953847Seh146360 
7963847Seh146360 	/*
7973847Seh146360 	 * configuration capabilities
7983847Seh146360 	 */
7993847Seh146360 	cfg.flags = IPW2100_CFG_BSS_MASK | IPW2100_CFG_IBSS_MASK |
8003847Seh146360 	    IPW2100_CFG_PREAMBLE_AUTO | IPW2100_CFG_802_1x_ENABLE;
8013847Seh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS)
8023847Seh146360 		cfg.flags |= IPW2100_CFG_IBSS_AUTO_START;
8033847Seh146360 	if (sc->if_flags & IFF_PROMISC)
8043847Seh146360 		cfg.flags |= IPW2100_CFG_PROMISCUOUS;
8053847Seh146360 	cfg.flags	= LE_32(cfg.flags);
8063847Seh146360 	cfg.bss_chan	= LE_32(sc->sc_chmask >> 1);
8073847Seh146360 	cfg.ibss_chan	= LE_32(sc->sc_chmask >> 1);
8083847Seh146360 
8093847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8103847Seh146360 	    "ipw2100_config(): Setting configuration to 0x%x\n",
8113847Seh146360 	    LE_32(cfg.flags)));
8123847Seh146360 
8133847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_CONFIGURATION,
8143847Seh146360 	    &cfg, sizeof (cfg));
8153847Seh146360 
8163847Seh146360 	if (err != DDI_SUCCESS)
8173847Seh146360 		return (err);
8183847Seh146360 
8193847Seh146360 	/*
8203847Seh146360 	 * set 802.11 Tx rates
8213847Seh146360 	 */
8223847Seh146360 	data = LE_32(0x3);  /* 1, 2 */
8233847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8243847Seh146360 	    "ipw2100_config(): Setting 802.11 Tx rates to 0x%x\n",
8253847Seh146360 	    LE_32(data)));
8263847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_BASIC_TX_RATES,
8273847Seh146360 	    &data, sizeof (data));
8283847Seh146360 	if (err != DDI_SUCCESS)
8293847Seh146360 		return (err);
8303847Seh146360 
8313847Seh146360 	/*
8323847Seh146360 	 * set 802.11b Tx rates
8333847Seh146360 	 */
8343847Seh146360 	data = LE_32(0xf);  /* 1, 2, 5.5, 11 */
8353847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8363847Seh146360 	    "ipw2100_config(): Setting 802.11b Tx rates to 0x%x\n",
8373847Seh146360 	    LE_32(data)));
8383847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_RATES, &data, sizeof (data));
8393847Seh146360 	if (err != DDI_SUCCESS)
8403847Seh146360 		return (err);
8413847Seh146360 
8423847Seh146360 	/*
8433847Seh146360 	 * set power mode
8443847Seh146360 	 */
8453847Seh146360 	data = LE_32(IPW2100_POWER_MODE_CAM);
8463847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8473847Seh146360 	    "ipw2100_config(): Setting power mode to %u\n", LE_32(data)));
8483847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_POWER_MODE, &data, sizeof (data));
8493847Seh146360 	if (err != DDI_SUCCESS)
8503847Seh146360 		return (err);
8513847Seh146360 
8523847Seh146360 	/*
8533847Seh146360 	 * set power index
8543847Seh146360 	 */
8553847Seh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
8563847Seh146360 		data = LE_32(32);
8573847Seh146360 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8583847Seh146360 		    "ipw2100_config(): Setting Tx power index to %u\n",
8593847Seh146360 		    LE_32(data)));
8603847Seh146360 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_POWER_INDEX,
8613847Seh146360 		    &data, sizeof (data));
8623847Seh146360 		if (err != DDI_SUCCESS)
8633847Seh146360 			return (err);
8643847Seh146360 	}
8653847Seh146360 
8663847Seh146360 	/*
8673847Seh146360 	 * set RTS threshold
8683847Seh146360 	 */
8693847Seh146360 	ic->ic_rtsthreshold = 2346;
8703847Seh146360 	data = LE_32(ic->ic_rtsthreshold);
8713847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8723847Seh146360 	    "ipw2100_config(): Setting RTS threshold to %u\n", LE_32(data)));
8733847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_RTS_THRESHOLD,
8743847Seh146360 	    &data, sizeof (data));
8753847Seh146360 	if (err != DDI_SUCCESS)
8763847Seh146360 		return (err);
8773847Seh146360 
8783847Seh146360 	/*
8793847Seh146360 	 * set frag threshold
8803847Seh146360 	 */
8813847Seh146360 	ic->ic_fragthreshold = 2346;
8823847Seh146360 	data = LE_32(ic->ic_fragthreshold);
8833847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8843847Seh146360 	    "ipw2100_config(): Setting frag threshold to %u\n", LE_32(data)));
8853847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_FRAG_THRESHOLD,
8863847Seh146360 	    &data, sizeof (data));
8873847Seh146360 	if (err != DDI_SUCCESS)
8883847Seh146360 		return (err);
8893847Seh146360 
8903847Seh146360 	/*
8913847Seh146360 	 * set ESSID
8923847Seh146360 	 */
8933847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
8943847Seh146360 	    "ipw2100_config(): Setting ESSID to %u, ESSID[0]%c\n",
8953847Seh146360 	    ic->ic_des_esslen, ic->ic_des_essid[0]));
8963847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_ESSID,
8973847Seh146360 	    ic->ic_des_essid, ic->ic_des_esslen);
8983847Seh146360 	if (err != DDI_SUCCESS)
8993847Seh146360 		return (err);
9003847Seh146360 
9013847Seh146360 	/*
9023847Seh146360 	 * no mandatory BSSID
9033847Seh146360 	 */
9043847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_MANDATORY_BSSID, NULL, 0);
9053847Seh146360 	if (err != DDI_SUCCESS)
9063847Seh146360 		return (err);
9073847Seh146360 
9083847Seh146360 	/*
9093847Seh146360 	 * set BSSID, if any
9103847Seh146360 	 */
9113847Seh146360 	if (ic->ic_flags & IEEE80211_F_DESBSSID) {
9123847Seh146360 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
9133847Seh146360 		    "ipw2100_config(): Setting BSSID to %u\n",
9143847Seh146360 		    IEEE80211_ADDR_LEN));
9153847Seh146360 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_DESIRED_BSSID,
9163847Seh146360 		    ic->ic_des_bssid, IEEE80211_ADDR_LEN);
9173847Seh146360 		if (err != DDI_SUCCESS)
9183847Seh146360 			return (err);
9193847Seh146360 	}
9203847Seh146360 
9213847Seh146360 	/*
9223847Seh146360 	 * set security information
9233847Seh146360 	 */
9243847Seh146360 	(void) memset(&sec, 0, sizeof (sec));
9253847Seh146360 	/*
9263847Seh146360 	 * use the value set to ic_bss to retrieve current sharedmode
9273847Seh146360 	 */
9283847Seh146360 	sec.authmode = (ic->ic_bss->in_authmode == WL_SHAREDKEY) ?
9293847Seh146360 	    IPW2100_AUTH_SHARED : IPW2100_AUTH_OPEN;
9303847Seh146360 	sec.ciphers = LE_32(IPW2100_CIPHER_NONE);
9313847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
9323847Seh146360 	    "ipw2100_config(): Setting authmode to %u\n", sec.authmode));
9333847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_SECURITY_INFORMATION,
9343847Seh146360 	    &sec, sizeof (sec));
9353847Seh146360 	if (err != DDI_SUCCESS)
9363847Seh146360 		return (err);
9373847Seh146360 
9383847Seh146360 	/*
9393847Seh146360 	 * set WEP if any
9403847Seh146360 	 */
9413847Seh146360 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
9423847Seh146360 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
9433847Seh146360 			if (ic->ic_nw_keys[i].wk_keylen == 0)
9443847Seh146360 				continue;
9453847Seh146360 			wkey.idx = (uint8_t)i;
9463847Seh146360 			wkey.len = ic->ic_nw_keys[i].wk_keylen;
9473847Seh146360 			(void) memset(wkey.key, 0, sizeof (wkey.key));
9483847Seh146360 			if (ic->ic_nw_keys[i].wk_keylen)
9493847Seh146360 				(void) memcpy(wkey.key,
9503847Seh146360 				    ic->ic_nw_keys[i].wk_key,
9513847Seh146360 				    ic->ic_nw_keys[i].wk_keylen);
9523847Seh146360 			err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY,
9533847Seh146360 			    &wkey, sizeof (wkey));
9543847Seh146360 			if (err != DDI_SUCCESS)
9553847Seh146360 				return (err);
9563847Seh146360 		}
9573847Seh146360 		data = LE_32(ic->ic_def_txkey);
9583847Seh146360 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY_INDEX,
9593847Seh146360 		    &data, sizeof (data));
9603847Seh146360 		if (err != DDI_SUCCESS)
9613847Seh146360 			return (err);
9623847Seh146360 	}
9633847Seh146360 
9643847Seh146360 	/*
9653847Seh146360 	 * turn on WEP
9663847Seh146360 	 */
9673847Seh146360 	data = LE_32((ic->ic_flags & IEEE80211_F_PRIVACY) ? 0x8 : 0);
9683847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
9693847Seh146360 	    "ipw2100_config(): Setting WEP flags to %u\n", LE_32(data)));
9703847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_FLAGS, &data, sizeof (data));
9713847Seh146360 	if (err != DDI_SUCCESS)
9723847Seh146360 		return (err);
9733847Seh146360 
9743847Seh146360 	/*
9753847Seh146360 	 * set beacon interval if IBSS or HostAP
9763847Seh146360 	 */
9773847Seh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS ||
9783847Seh146360 	    ic->ic_opmode == IEEE80211_M_HOSTAP) {
9793847Seh146360 
9803847Seh146360 		data = LE_32(ic->ic_lintval);
9813847Seh146360 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
9823847Seh146360 		    "ipw2100_config(): Setting beacon interval to %u\n",
9833847Seh146360 		    LE_32(data)));
9843847Seh146360 		err = ipw2100_cmd(sc, IPW2100_CMD_SET_BEACON_INTERVAL,
9853847Seh146360 		    &data, sizeof (data));
9863847Seh146360 		if (err != DDI_SUCCESS)
9873847Seh146360 			return (err);
9883847Seh146360 	}
9893847Seh146360 
9903847Seh146360 	/*
9913847Seh146360 	 * set scan options
9923847Seh146360 	 */
9933847Seh146360 	sopt.flags = LE_32(0);
9943847Seh146360 	sopt.channels = LE_32(sc->sc_chmask >> 1);
9953847Seh146360 	err = ipw2100_cmd(sc, IPW2100_CMD_SET_SCAN_OPTIONS,
9963847Seh146360 	    &sopt, sizeof (sopt));
9973847Seh146360 	if (err != DDI_SUCCESS)
9983847Seh146360 		return (err);
9993847Seh146360 
10003847Seh146360 en_adapter:
10013847Seh146360 
10023847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
10033847Seh146360 	    "ipw2100_config(): Enabling adapter\n"));
10043847Seh146360 
10053847Seh146360 	return (ipw2100_cmd(sc, IPW2100_CMD_ENABLE, NULL, 0));
10063847Seh146360 }
10073847Seh146360 
10083847Seh146360 static int
ipw2100_cmd(struct ipw2100_softc * sc,uint32_t type,void * buf,size_t len)10093847Seh146360 ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type, void *buf, size_t len)
10103847Seh146360 {
10113847Seh146360 	struct ipw2100_bd	*txbd;
10123847Seh146360 	clock_t			clk;
10133847Seh146360 	uint32_t		idx;
10143847Seh146360 
10153847Seh146360 	/*
10163847Seh146360 	 * prepare command buffer
10173847Seh146360 	 */
10183847Seh146360 	sc->sc_cmd->type = LE_32(type);
10193847Seh146360 	sc->sc_cmd->subtype = LE_32(0);
10203847Seh146360 	sc->sc_cmd->seq = LE_32(0);
10213847Seh146360 	/*
10223847Seh146360 	 * copy data if any
10233847Seh146360 	 */
10243847Seh146360 	if (len && buf)
10253847Seh146360 		(void) memcpy(sc->sc_cmd->data, buf, len);
10263847Seh146360 	sc->sc_cmd->len = LE_32(len);
10273847Seh146360 
10283847Seh146360 	/*
10293847Seh146360 	 * get host & device descriptor to submit command
10303847Seh146360 	 */
10313847Seh146360 	mutex_enter(&sc->sc_tx_lock);
10323847Seh146360 
10333847Seh146360 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
10343847Seh146360 	    "ipw2100_cmd(): tx-free=%d\n", sc->sc_tx_free));
10353847Seh146360 
10363847Seh146360 	/*
10373847Seh146360 	 * command need 1 descriptor
10383847Seh146360 	 */
10393847Seh146360 	while (sc->sc_tx_free < 1)  {
10403847Seh146360 		sc->sc_flags |= IPW2100_FLAG_CMD_WAIT;
10413847Seh146360 		cv_wait(&sc->sc_tx_cond, &sc->sc_tx_lock);
10423847Seh146360 	}
10433847Seh146360 	idx = sc->sc_tx_cur;
10443847Seh146360 
10453847Seh146360 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
10463847Seh146360 	    "ipw2100_cmd(): tx-cur=%d\n", idx));
10473847Seh146360 
10483847Seh146360 	sc->sc_done = 0;
10493847Seh146360 
10503847Seh146360 	txbd		= &sc->sc_txbd[idx];
10513847Seh146360 	txbd->phyaddr	= LE_32(sc->sc_dma_cmd.dr_pbase);
10523847Seh146360 	txbd->len	= LE_32(sizeof (struct ipw2100_cmd));
10533847Seh146360 	txbd->flags	= IPW2100_BD_FLAG_TX_FRAME_COMMAND
10543847Seh146360 	    | IPW2100_BD_FLAG_TX_LAST_FRAGMENT;
10553847Seh146360 	txbd->nfrag	= 1;
10563847Seh146360 	/*
10573847Seh146360 	 * sync for device
10583847Seh146360 	 */
10593847Seh146360 	(void) ddi_dma_sync(sc->sc_dma_cmd.dr_hnd, 0,
10603847Seh146360 	    sizeof (struct ipw2100_cmd), DDI_DMA_SYNC_FORDEV);
10613847Seh146360 	(void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd,
10623847Seh146360 	    idx * sizeof (struct ipw2100_bd),
10633847Seh146360 	    sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
10643847Seh146360 
10653847Seh146360 	/*
10663847Seh146360 	 * ring move forward
10673847Seh146360 	 */
10683847Seh146360 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD);
10693847Seh146360 	sc->sc_tx_free--;
10703847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur);
10713847Seh146360 	mutex_exit(&sc->sc_tx_lock);
10723847Seh146360 
10733847Seh146360 	/*
10743847Seh146360 	 * wait for command done
10753847Seh146360 	 */
107611066Srafael.vanoni@sun.com 	clk = drv_usectohz(1000000);	/* 1 second */
10773847Seh146360 	mutex_enter(&sc->sc_ilock);
10783847Seh146360 	while (sc->sc_done == 0) {
10793847Seh146360 		/*
10803847Seh146360 		 * pending for the response
10813847Seh146360 		 */
108211066Srafael.vanoni@sun.com 		if (cv_reltimedwait(&sc->sc_cmd_cond, &sc->sc_ilock,
108311066Srafael.vanoni@sun.com 		    clk, TR_CLOCK_TICK) < 0)
10843847Seh146360 			break;
10853847Seh146360 	}
10863847Seh146360 	mutex_exit(&sc->sc_ilock);
10873847Seh146360 
10883847Seh146360 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
10893847Seh146360 	    "ipw2100_cmd(): cmd-done=%s\n", sc->sc_done ? "yes" : "no"));
10903847Seh146360 
10913847Seh146360 	if (sc->sc_done == 0)
10923847Seh146360 		return (DDI_FAILURE);
10933847Seh146360 
10943847Seh146360 	return (DDI_SUCCESS);
10953847Seh146360 }
10963847Seh146360 
10973847Seh146360 int
ipw2100_init(struct ipw2100_softc * sc)10983847Seh146360 ipw2100_init(struct ipw2100_softc *sc)
10993847Seh146360 {
11003847Seh146360 	int	err;
11013847Seh146360 
11028550SSeth.Goldberg@Sun.COM 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
11038550SSeth.Goldberg@Sun.COM 	    "ipw2100_init(): enter\n"));
11048550SSeth.Goldberg@Sun.COM 
11053847Seh146360 	/*
11063847Seh146360 	 * no firmware is available, return fail directly
11073847Seh146360 	 */
11083847Seh146360 	if (!(sc->sc_flags & IPW2100_FLAG_FW_CACHED)) {
11093847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
11103847Seh146360 		    "ipw2100_init(): no firmware is available\n"));
11113847Seh146360 		return (DDI_FAILURE);
11123847Seh146360 	}
11133847Seh146360 
11143847Seh146360 	ipw2100_stop(sc);
11153847Seh146360 
11163847Seh146360 	err = ipw2100_chip_reset(sc);
11173847Seh146360 	if (err != DDI_SUCCESS) {
11183847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
11193847Seh146360 		    "ipw2100_init(): could not reset adapter\n"));
11203847Seh146360 		goto fail;
11213847Seh146360 	}
11223847Seh146360 
11233847Seh146360 	/*
11243847Seh146360 	 * load microcode
11253847Seh146360 	 */
11268550SSeth.Goldberg@Sun.COM 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
11278550SSeth.Goldberg@Sun.COM 	    "ipw2100_init(): loading microcode\n"));
11283847Seh146360 	err = ipw2100_load_uc(sc);
11293847Seh146360 	if (err != DDI_SUCCESS) {
11303847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
11313847Seh146360 		    "ipw2100_init(): could not load microcode, try again\n"));
11323847Seh146360 		goto fail;
11333847Seh146360 	}
11343847Seh146360 
11353847Seh146360 	ipw2100_master_stop(sc);
11363847Seh146360 
11373847Seh146360 	ipw2100_ring_hwsetup(sc);
11383847Seh146360 
11393847Seh146360 	/*
11403847Seh146360 	 * load firmware
11413847Seh146360 	 */
11428550SSeth.Goldberg@Sun.COM 	IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT,
11438550SSeth.Goldberg@Sun.COM 	    "ipw2100_init(): loading firmware\n"));
11443847Seh146360 	err = ipw2100_load_fw(sc);
11453847Seh146360 	if (err != DDI_SUCCESS) {
11463847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
11473847Seh146360 		    "ipw2100_init(): could not load firmware, try again\n"));
11483847Seh146360 		goto fail;
11493847Seh146360 	}
11503847Seh146360 
11513847Seh146360 	/*
11523847Seh146360 	 * initialize tables
11533847Seh146360 	 */
11543847Seh146360 	ipw2100_tables_init(sc);
11553847Seh146360 	ipw2100_table1_put32(sc, IPW2100_INFO_LOCK, 0);
11563847Seh146360 
11573847Seh146360 	/*
11583847Seh146360 	 * Hardware will be enabled after configuration
11593847Seh146360 	 */
11603847Seh146360 	err = ipw2100_config(sc);
11613847Seh146360 	if (err != DDI_SUCCESS) {
11623847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
11633847Seh146360 		    "ipw2100_init(): device configuration failed\n"));
11643847Seh146360 		goto fail;
11653847Seh146360 	}
11663847Seh146360 
11673847Seh146360 	delay(drv_usectohz(delay_config_stable));
11683847Seh146360 
11693847Seh146360 	return (DDI_SUCCESS);
11703847Seh146360 
11713847Seh146360 fail:
11723847Seh146360 	ipw2100_stop(sc);
11733847Seh146360 
11743847Seh146360 	return (err);
11753847Seh146360 }
11763847Seh146360 
11773847Seh146360 /*
11783847Seh146360  * get hardware configurations from EEPROM embedded within chip
11793847Seh146360  */
11803847Seh146360 static void
ipw2100_hwconf_get(struct ipw2100_softc * sc)11813847Seh146360 ipw2100_hwconf_get(struct ipw2100_softc *sc)
11823847Seh146360 {
11833847Seh146360 	int		i;
11843847Seh146360 	uint16_t	val;
11853847Seh146360 
11863847Seh146360 	/*
11873847Seh146360 	 * MAC address
11883847Seh146360 	 */
11893847Seh146360 	i = 0;
11903847Seh146360 	val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 0);
11913847Seh146360 	sc->sc_macaddr[i++] = val >> 8;
11923847Seh146360 	sc->sc_macaddr[i++] = val & 0xff;
11933847Seh146360 	val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 1);
11943847Seh146360 	sc->sc_macaddr[i++] = val >> 8;
11953847Seh146360 	sc->sc_macaddr[i++] = val & 0xff;
11963847Seh146360 	val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 2);
11973847Seh146360 	sc->sc_macaddr[i++] = val >> 8;
11983847Seh146360 	sc->sc_macaddr[i++] = val & 0xff;
11993847Seh146360 
12003847Seh146360 	/*
12013847Seh146360 	 * formatted MAC address string
12023847Seh146360 	 */
12033847Seh146360 	(void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr),
12044784Seh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x",
12054784Seh146360 	    sc->sc_macaddr[0], sc->sc_macaddr[1],
12064784Seh146360 	    sc->sc_macaddr[2], sc->sc_macaddr[3],
12074784Seh146360 	    sc->sc_macaddr[4], sc->sc_macaddr[5]);
12083847Seh146360 
12093847Seh146360 	/*
12103847Seh146360 	 * channel mask
12113847Seh146360 	 */
12123847Seh146360 	val = ipw2100_rom_get16(sc, IPW2100_ROM_CHANNEL_LIST);
12133847Seh146360 	if (val == 0)
12143847Seh146360 		val = 0x7ff;
12153847Seh146360 	sc->sc_chmask = val << 1;
12163847Seh146360 	IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT,
12173847Seh146360 	    "ipw2100_hwconf_get(): channel-mask=0x%08x\n", sc->sc_chmask));
12183847Seh146360 
12193847Seh146360 	/*
12203847Seh146360 	 * radio switch
12213847Seh146360 	 */
12223847Seh146360 	val = ipw2100_rom_get16(sc, IPW2100_ROM_RADIO);
12233847Seh146360 	if (val & 0x08)
12243847Seh146360 		sc->sc_flags |= IPW2100_FLAG_HAS_RADIO_SWITCH;
12253847Seh146360 
12263847Seh146360 	IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT,
12273847Seh146360 	    "ipw2100_hwconf_get(): has-radio-switch=%s(%u)\n",
12283847Seh146360 	    (sc->sc_flags & IPW2100_FLAG_HAS_RADIO_SWITCH)?  "yes" : "no",
12293847Seh146360 	    val));
12303847Seh146360 }
12313847Seh146360 
12323847Seh146360 /*
12333847Seh146360  * all ipw2100 interrupts will be masked by this routine
12343847Seh146360  */
12353847Seh146360 static void
ipw2100_master_stop(struct ipw2100_softc * sc)12363847Seh146360 ipw2100_master_stop(struct ipw2100_softc *sc)
12373847Seh146360 {
12383847Seh146360 	uint32_t	tmp;
12393847Seh146360 	int		ntries;
12403847Seh146360 
12413847Seh146360 	/*
12423847Seh146360 	 * disable interrupts
12433847Seh146360 	 */
12443847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0);
12453847Seh146360 
12463847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_STOP_MASTER);
12473847Seh146360 	for (ntries = 0; ntries < 50; ntries++) {
12483847Seh146360 		if (ipw2100_csr_get32(sc, IPW2100_CSR_RST)
12493847Seh146360 		    & IPW2100_RST_MASTER_DISABLED)
12503847Seh146360 			break;
12513847Seh146360 		drv_usecwait(10);
12523847Seh146360 	}
12537804SKonstantin.Ananyev@Sun.COM 	if (ntries == 50 && !(sc->sc_flags & IPW2100_FLAG_QUIESCED))
12543847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
12553847Seh146360 		    "ipw2100_master_stop(): timeout when stop master\n"));
12563847Seh146360 
12573847Seh146360 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST);
12583847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST,
12593847Seh146360 	    tmp | IPW2100_RST_PRINCETON_RESET);
12603847Seh146360 
12613847Seh146360 	sc->sc_flags &= ~IPW2100_FLAG_FW_INITED;
12623847Seh146360 }
12633847Seh146360 
12643847Seh146360 /*
12653847Seh146360  * all ipw2100 interrupts will be masked by this routine
12663847Seh146360  */
12673847Seh146360 static int
ipw2100_chip_reset(struct ipw2100_softc * sc)12683847Seh146360 ipw2100_chip_reset(struct ipw2100_softc *sc)
12693847Seh146360 {
12703847Seh146360 	int		ntries;
12713847Seh146360 	uint32_t	tmp;
12723847Seh146360 
12733847Seh146360 	ipw2100_master_stop(sc);
12743847Seh146360 
12753847Seh146360 	/*
12763847Seh146360 	 * move adatper to DO state
12773847Seh146360 	 */
12783847Seh146360 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL);
12793847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT);
12803847Seh146360 
12813847Seh146360 	/*
12823847Seh146360 	 * wait for clock stabilization
12833847Seh146360 	 */
12843847Seh146360 	for (ntries = 0; ntries < 1000; ntries++) {
12853847Seh146360 		if (ipw2100_csr_get32(sc, IPW2100_CSR_CTL)
12863847Seh146360 		    & IPW2100_CTL_CLOCK_READY)
12873847Seh146360 			break;
12883847Seh146360 		drv_usecwait(200);
12893847Seh146360 	}
12903847Seh146360 	if (ntries == 1000)
12913847Seh146360 		return (DDI_FAILURE);
12923847Seh146360 
12933847Seh146360 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST);
12943847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RST, tmp | IPW2100_RST_SW_RESET);
12953847Seh146360 
12963847Seh146360 	drv_usecwait(10);
12973847Seh146360 
12983847Seh146360 	tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL);
12993847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT);
13003847Seh146360 
13013847Seh146360 	return (DDI_SUCCESS);
13023847Seh146360 }
13033847Seh146360 
13043847Seh146360 /*
13053847Seh146360  * get the radio status from IPW_CSR_IO, invoked by wificonfig/dladm
13063847Seh146360  */
13073847Seh146360 int
ipw2100_get_radio(struct ipw2100_softc * sc)13083847Seh146360 ipw2100_get_radio(struct ipw2100_softc *sc)
13093847Seh146360 {
13103847Seh146360 	if (ipw2100_csr_get32(sc, IPW2100_CSR_IO) & IPW2100_IO_RADIO_DISABLED)
13113847Seh146360 		return (0);
13123847Seh146360 	else
13133847Seh146360 		return (1);
13143847Seh146360 
13153847Seh146360 }
13163847Seh146360 /*
13173847Seh146360  * This function is used to get the statistic, invoked by wificonfig/dladm
13183847Seh146360  */
13193847Seh146360 void
ipw2100_get_statistics(struct ipw2100_softc * sc)13203847Seh146360 ipw2100_get_statistics(struct ipw2100_softc *sc)
13213847Seh146360 {
13223847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
13233847Seh146360 	uint32_t		addr, size, i;
13243847Seh146360 	uint32_t		atbl[256], *datatbl;
13253847Seh146360 
13263847Seh146360 	datatbl = atbl;
13273847Seh146360 
13283847Seh146360 	if (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) {
13293847Seh146360 		IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT,
13303847Seh146360 		    "ipw2100_get_statistic(): fw doesn't download yet."));
13313847Seh146360 		return;
13323847Seh146360 	}
13333847Seh146360 
13343847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_AUTOINC_ADDR, sc->sc_table1_base);
13353847Seh146360 
13363847Seh146360 	size = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA);
13373847Seh146360 	atbl[0] = size;
13383847Seh146360 	for (i = 1, ++datatbl; i < size; i++, datatbl++) {
13393847Seh146360 		addr = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA);
13403847Seh146360 		*datatbl = ipw2100_imem_get32(sc, addr);
13413847Seh146360 	}
13423847Seh146360 
13433847Seh146360 	/*
13443847Seh146360 	 * To retrieve the statistic information into proper places. There are
13453847Seh146360 	 * lot of information.
13463847Seh146360 	 */
13473847Seh146360 	IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT,
13483847Seh146360 	    "ipw2100_get_statistic(): \n"
13493847Seh146360 	    "operating mode = %u\n"
13503847Seh146360 	    "type of authentification= %u\n"
13513847Seh146360 	    "average RSSI= %u\n"
13523847Seh146360 	    "current channel = %d\n",
13533847Seh146360 	    atbl[191], atbl[199], atbl[173], atbl[189]));
13543847Seh146360 	/* WIFI_STAT_TX_FRAGS */
13553847Seh146360 	ic->ic_stats.is_tx_frags = (uint32_t)atbl[2];
13563847Seh146360 	/* WIFI_STAT_MCAST_TX = (all frame - unicast frame) */
13573847Seh146360 	ic->ic_stats.is_tx_mcast = (uint32_t)atbl[2] - (uint32_t)atbl[3];
13583847Seh146360 	/* WIFI_STAT_TX_RETRANS */
13593847Seh146360 	ic->ic_stats.is_tx_retries = (uint32_t)atbl[42];
13603847Seh146360 	/* WIFI_STAT_TX_FAILED */
13613847Seh146360 	ic->ic_stats.is_tx_failed = (uint32_t)atbl[51];
13623847Seh146360 	/* MAC_STAT_OBYTES */
13633847Seh146360 	ic->ic_stats.is_tx_bytes = (uint32_t)atbl[41];
13643847Seh146360 	/* WIFI_STAT_RX_FRAGS */
13653847Seh146360 	ic->ic_stats.is_rx_frags = (uint32_t)atbl[61];
13663847Seh146360 	/* WIFI_STAT_MCAST_RX */
13673847Seh146360 	ic->ic_stats.is_rx_mcast = (uint32_t)atbl[71];
13683847Seh146360 	/* MAC_STAT_IBYTES */
13693847Seh146360 	ic->ic_stats.is_rx_bytes = (uint32_t)atbl[101];
13703847Seh146360 	/* WIFI_STAT_ACK_FAILURE */
13713847Seh146360 	ic->ic_stats.is_ack_failure = (uint32_t)atbl[59];
13723847Seh146360 	/* WIFI_STAT_RTS_SUCCESS */
13733847Seh146360 	ic->ic_stats.is_rts_success = (uint32_t)atbl[22];
13743847Seh146360 }
13753847Seh146360 
13763847Seh146360 /*
13773847Seh146360  * dma region alloc
13783847Seh146360  */
13793847Seh146360 static int
ipw2100_dma_region_alloc(struct ipw2100_softc * sc,struct dma_region * dr,size_t size,uint_t dir,uint_t flags)13803847Seh146360 ipw2100_dma_region_alloc(struct ipw2100_softc *sc,
13813847Seh146360     struct dma_region *dr, size_t size, uint_t dir, uint_t flags)
13823847Seh146360 {
13833847Seh146360 	dev_info_t	*dip = sc->sc_dip;
13843847Seh146360 	int		err;
13853847Seh146360 
13863847Seh146360 	IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
13873847Seh146360 	    "ipw2100_dma_region_alloc() name=%s size=%u\n",
13883847Seh146360 	    dr->dr_name, size));
13893847Seh146360 
13903847Seh146360 	err = ddi_dma_alloc_handle(dip, &ipw2100_dma_attr, DDI_DMA_SLEEP, NULL,
13913847Seh146360 	    &dr->dr_hnd);
13923847Seh146360 	if (err != DDI_SUCCESS) {
13933847Seh146360 		IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
13943847Seh146360 		    "ipw2100_dma_region_alloc(): "
13953847Seh146360 		    "ddi_dma_alloc_handle() failed\n"));
13963847Seh146360 		goto fail0;
13973847Seh146360 	}
13983847Seh146360 
13993847Seh146360 	err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2100_dma_accattr,
14003847Seh146360 	    flags, DDI_DMA_SLEEP, NULL, &dr->dr_base,
14013847Seh146360 	    &dr->dr_size, &dr->dr_acc);
14023847Seh146360 	if (err != DDI_SUCCESS) {
14033847Seh146360 		IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
14043847Seh146360 		    "ipw2100_dma_region_alloc(): "
14053847Seh146360 		    "ddi_dma_mem_alloc() failed\n"));
14063847Seh146360 		goto fail1;
14073847Seh146360 	}
14083847Seh146360 
14093847Seh146360 	err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL,
14103847Seh146360 	    dr->dr_base, dr->dr_size, dir | flags, DDI_DMA_SLEEP, NULL,
14113847Seh146360 	    &dr->dr_cookie, &dr->dr_ccnt);
14123847Seh146360 	if (err != DDI_DMA_MAPPED) {
14133847Seh146360 		IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
14143847Seh146360 		    "ipw2100_dma_region_alloc(): "
14153847Seh146360 		    "ddi_dma_addr_bind_handle() failed\n"));
14163847Seh146360 		goto fail2;
14173847Seh146360 	}
14183847Seh146360 
14193847Seh146360 	if (dr->dr_ccnt != 1) {
14203847Seh146360 		err = DDI_FAILURE;
14213847Seh146360 		goto fail3;
14223847Seh146360 	}
14233847Seh146360 	dr->dr_pbase = dr->dr_cookie.dmac_address;
14243847Seh146360 
14253847Seh146360 	IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT,
14263847Seh146360 	    "ipw2100_dma_region_alloc(): get physical-base=0x%08x\n",
14273847Seh146360 	    dr->dr_pbase));
14283847Seh146360 
14293847Seh146360 	return (DDI_SUCCESS);
14303847Seh146360 
14313847Seh146360 fail3:
14323847Seh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
14333847Seh146360 fail2:
14343847Seh146360 	ddi_dma_mem_free(&dr->dr_acc);
14353847Seh146360 fail1:
14363847Seh146360 	ddi_dma_free_handle(&dr->dr_hnd);
14373847Seh146360 fail0:
14383847Seh146360 	return (err);
14393847Seh146360 }
14403847Seh146360 
14413847Seh146360 static void
ipw2100_dma_region_free(struct dma_region * dr)14423847Seh146360 ipw2100_dma_region_free(struct dma_region *dr)
14433847Seh146360 {
14443847Seh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
14453847Seh146360 	ddi_dma_mem_free(&dr->dr_acc);
14463847Seh146360 	ddi_dma_free_handle(&dr->dr_hnd);
14473847Seh146360 }
14483847Seh146360 
14493847Seh146360 static int
ipw2100_ring_alloc(struct ipw2100_softc * sc)14503847Seh146360 ipw2100_ring_alloc(struct ipw2100_softc *sc)
14513847Seh146360 {
14523847Seh146360 	int	err, i;
14533847Seh146360 
14543847Seh146360 	/*
14553847Seh146360 	 * tx ring
14563847Seh146360 	 */
14573847Seh146360 	sc->sc_dma_txbd.dr_name = "ipw2100-tx-ring-bd";
14583847Seh146360 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbd,
14593847Seh146360 	    IPW2100_TXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
14603847Seh146360 	if (err != DDI_SUCCESS)
14613847Seh146360 		goto fail0;
14623847Seh146360 	/*
14633847Seh146360 	 * tx bufs
14643847Seh146360 	 */
14653847Seh146360 	for (i = 0; i < IPW2100_NUM_TXBUF; i++) {
14663847Seh146360 		sc->sc_dma_txbufs[i].dr_name = "ipw2100-tx-buf";
14673847Seh146360 		err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbufs[i],
14683847Seh146360 		    IPW2100_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING);
14693847Seh146360 		if (err != DDI_SUCCESS) {
14707892SPengcheng.Chen@Sun.COM 			while (i > 0) {
14717892SPengcheng.Chen@Sun.COM 				i--;
14723847Seh146360 				ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]);
14733847Seh146360 			}
14743847Seh146360 			goto fail1;
14753847Seh146360 		}
14763847Seh146360 	}
14773847Seh146360 	/*
14783847Seh146360 	 * rx ring
14793847Seh146360 	 */
14803847Seh146360 	sc->sc_dma_rxbd.dr_name = "ipw2100-rx-ring-bd";
14813847Seh146360 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbd,
14823847Seh146360 	    IPW2100_RXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
14833847Seh146360 	if (err != DDI_SUCCESS)
14843847Seh146360 		goto fail2;
14853847Seh146360 	/*
14863847Seh146360 	 * rx bufs
14873847Seh146360 	 */
14883847Seh146360 	for (i = 0; i < IPW2100_NUM_RXBUF; i++) {
14893847Seh146360 		sc->sc_dma_rxbufs[i].dr_name = "ipw2100-rx-buf";
14903847Seh146360 		err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i],
14913847Seh146360 		    IPW2100_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING);
14923847Seh146360 		if (err != DDI_SUCCESS) {
14937892SPengcheng.Chen@Sun.COM 			while (i > 0) {
14947892SPengcheng.Chen@Sun.COM 				i--;
14953847Seh146360 				ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]);
14963847Seh146360 			}
14973847Seh146360 			goto fail3;
14983847Seh146360 		}
14993847Seh146360 	}
15003847Seh146360 	/*
15013847Seh146360 	 * status
15023847Seh146360 	 */
15033847Seh146360 	sc->sc_dma_status.dr_name = "ipw2100-rx-status";
15043847Seh146360 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_status,
15053847Seh146360 	    IPW2100_STATUS_SIZE, DDI_DMA_READ, DDI_DMA_CONSISTENT);
15063847Seh146360 	if (err != DDI_SUCCESS)
15073847Seh146360 		goto fail4;
15083847Seh146360 	/*
15093847Seh146360 	 * command
15103847Seh146360 	 */
15113847Seh146360 	sc->sc_dma_cmd.dr_name = "ipw2100-cmd";
15123847Seh146360 	err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_cmd, IPW2100_CMD_SIZE,
15133847Seh146360 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
15143847Seh146360 	if (err != DDI_SUCCESS)
15153847Seh146360 		goto fail5;
15163847Seh146360 
15173847Seh146360 	return (DDI_SUCCESS);
15183847Seh146360 
15193847Seh146360 fail5:
15203847Seh146360 	ipw2100_dma_region_free(&sc->sc_dma_status);
15213847Seh146360 fail4:
15223847Seh146360 	for (i = 0; i < IPW2100_NUM_RXBUF; i++)
15233847Seh146360 		ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]);
15243847Seh146360 fail3:
15253847Seh146360 	ipw2100_dma_region_free(&sc->sc_dma_rxbd);
15263847Seh146360 fail2:
15273847Seh146360 	for (i = 0; i < IPW2100_NUM_TXBUF; i++)
15283847Seh146360 		ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]);
15293847Seh146360 fail1:
15303847Seh146360 	ipw2100_dma_region_free(&sc->sc_dma_txbd);
15313847Seh146360 fail0:
15323847Seh146360 	return (err);
15333847Seh146360 }
15343847Seh146360 
15353847Seh146360 static void
ipw2100_ring_free(struct ipw2100_softc * sc)15363847Seh146360 ipw2100_ring_free(struct ipw2100_softc *sc)
15373847Seh146360 {
15383847Seh146360 	int	i;
15393847Seh146360 
15403847Seh146360 	/*
15413847Seh146360 	 * tx ring
15423847Seh146360 	 */
15433847Seh146360 	ipw2100_dma_region_free(&sc->sc_dma_txbd);
15443847Seh146360 	/*
15453847Seh146360 	 * tx buf
15463847Seh146360 	 */
15473847Seh146360 	for (i = 0; i < IPW2100_NUM_TXBUF; i++)
15483847Seh146360 		ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]);
15493847Seh146360 	/*
15503847Seh146360 	 * rx ring
15513847Seh146360 	 */
15523847Seh146360 	ipw2100_dma_region_free(&sc->sc_dma_rxbd);
15533847Seh146360 	/*
15543847Seh146360 	 * rx buf
15553847Seh146360 	 */
15563847Seh146360 	for (i = 0; i < IPW2100_NUM_RXBUF; i++)
15573847Seh146360 		ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]);
15583847Seh146360 	/*
15593847Seh146360 	 * status
15603847Seh146360 	 */
15613847Seh146360 	ipw2100_dma_region_free(&sc->sc_dma_status);
15623847Seh146360 	/*
15633847Seh146360 	 * command
15643847Seh146360 	 */
15653847Seh146360 	ipw2100_dma_region_free(&sc->sc_dma_cmd);
15663847Seh146360 }
15673847Seh146360 
15683847Seh146360 static void
ipw2100_ring_reset(struct ipw2100_softc * sc)15693847Seh146360 ipw2100_ring_reset(struct ipw2100_softc *sc)
15703847Seh146360 {
15713847Seh146360 	int	i;
15723847Seh146360 
15733847Seh146360 	/*
15743847Seh146360 	 * tx ring
15753847Seh146360 	 */
15763847Seh146360 	sc->sc_tx_cur   = 0;
15773847Seh146360 	sc->sc_tx_free  = IPW2100_NUM_TXBD;
15783847Seh146360 	sc->sc_txbd	= (struct ipw2100_bd *)sc->sc_dma_txbd.dr_base;
15793847Seh146360 	for (i = 0; i < IPW2100_NUM_TXBUF; i++)
15803847Seh146360 		sc->sc_txbufs[i] =
15813847Seh146360 		    (struct ipw2100_txb *)sc->sc_dma_txbufs[i].dr_base;
15823847Seh146360 	/*
15833847Seh146360 	 * rx ring
15843847Seh146360 	 */
15853847Seh146360 	sc->sc_rx_cur   = 0;
15863847Seh146360 	sc->sc_rx_free  = IPW2100_NUM_RXBD;
15873847Seh146360 	sc->sc_status   = (struct ipw2100_status *)sc->sc_dma_status.dr_base;
15883847Seh146360 	sc->sc_rxbd	= (struct ipw2100_bd *)sc->sc_dma_rxbd.dr_base;
15893847Seh146360 	for (i = 0; i < IPW2100_NUM_RXBUF; i++) {
15903847Seh146360 		sc->sc_rxbufs[i] =
15913847Seh146360 		    (struct ipw2100_rxb *)sc->sc_dma_rxbufs[i].dr_base;
15923847Seh146360 		/*
15933847Seh146360 		 * initialize Rx buffer descriptors, both host and device
15943847Seh146360 		 */
15953847Seh146360 		sc->sc_rxbd[i].phyaddr  = LE_32(sc->sc_dma_rxbufs[i].dr_pbase);
15963847Seh146360 		sc->sc_rxbd[i].len	= LE_32(sc->sc_dma_rxbufs[i].dr_size);
15973847Seh146360 		sc->sc_rxbd[i].flags	= 0;
15983847Seh146360 		sc->sc_rxbd[i].nfrag	= 1;
15993847Seh146360 	}
16003847Seh146360 	/*
16013847Seh146360 	 * command
16023847Seh146360 	 */
16033847Seh146360 	sc->sc_cmd = (struct ipw2100_cmd *)sc->sc_dma_cmd.dr_base;
16043847Seh146360 }
16053847Seh146360 
16063847Seh146360 /*
16073847Seh146360  * tx, rx rings and command initialization
16083847Seh146360  */
16093847Seh146360 static int
ipw2100_ring_init(struct ipw2100_softc * sc)16103847Seh146360 ipw2100_ring_init(struct ipw2100_softc *sc)
16113847Seh146360 {
16123847Seh146360 	int	err;
16133847Seh146360 
16143847Seh146360 	err = ipw2100_ring_alloc(sc);
16153847Seh146360 	if (err != DDI_SUCCESS)
16163847Seh146360 		return (err);
16173847Seh146360 
16183847Seh146360 	ipw2100_ring_reset(sc);
16193847Seh146360 
16203847Seh146360 	return (DDI_SUCCESS);
16213847Seh146360 }
16223847Seh146360 
16233847Seh146360 static void
ipw2100_ring_hwsetup(struct ipw2100_softc * sc)16243847Seh146360 ipw2100_ring_hwsetup(struct ipw2100_softc *sc)
16253847Seh146360 {
16263847Seh146360 	ipw2100_ring_reset(sc);
16273847Seh146360 	/*
16283847Seh146360 	 * tx ring
16293847Seh146360 	 */
16303847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_BASE, sc->sc_dma_txbd.dr_pbase);
16313847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_SIZE, IPW2100_NUM_TXBD);
16323847Seh146360 	/*
16333847Seh146360 	 * no new packet to transmit, tx-rd-index == tx-wr-index
16343847Seh146360 	 */
16353847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_READ_INDEX, sc->sc_tx_cur);
16363847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur);
16373847Seh146360 	/*
16383847Seh146360 	 * rx ring
16393847Seh146360 	 */
16403847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_BASE, sc->sc_dma_rxbd.dr_pbase);
16413847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_SIZE, IPW2100_NUM_RXBD);
16423847Seh146360 	/*
16433847Seh146360 	 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
16443847Seh146360 	 */
16453847Seh146360 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
16463847Seh146360 	    "ipw2100_ring_hwsetup(): rx-cur=%u, backward=%u\n",
16473847Seh146360 	    sc->sc_rx_cur, RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)));
16483847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_READ_INDEX, sc->sc_rx_cur);
16493847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX,
16503847Seh146360 	    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD));
16513847Seh146360 	/*
16523847Seh146360 	 * status
16533847Seh146360 	 */
16543847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_RX_STATUS_BASE,
16553847Seh146360 	    sc->sc_dma_status.dr_pbase);
16563847Seh146360 }
16573847Seh146360 
16583847Seh146360 /*
16593847Seh146360  * ieee80211_new_state() is not be used, since the hardware can handle the
16603847Seh146360  * state transfer. Here, we just keep the status of the hardware notification
16613847Seh146360  * result.
16623847Seh146360  */
16633847Seh146360 /* ARGSUSED */
16643847Seh146360 static int
ipw2100_newstate(struct ieee80211com * ic,enum ieee80211_state state,int arg)16653847Seh146360 ipw2100_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg)
16663847Seh146360 {
16673847Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)ic;
16683847Seh146360 	struct ieee80211_node	*in;
16693847Seh146360 	uint8_t			macaddr[IEEE80211_ADDR_LEN];
16703847Seh146360 	uint32_t		len;
16713847Seh146360 	wifi_data_t		wd = { 0 };
16723847Seh146360 
16733847Seh146360 	IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
16743847Seh146360 	    "ipw2100_newstate(): %s -> %s\n",
16753847Seh146360 	    ieee80211_state_name[ic->ic_state], ieee80211_state_name[state]));
16763847Seh146360 
16773847Seh146360 	switch (state) {
16783847Seh146360 	case IEEE80211_S_RUN:
16793847Seh146360 		/*
16803847Seh146360 		 * we only need to use BSSID as to find the node
16813847Seh146360 		 */
16823847Seh146360 		drv_usecwait(200); /* firmware needs a short delay here */
16833847Seh146360 		len = IEEE80211_ADDR_LEN;
16843847Seh146360 		(void) ipw2100_table2_getbuf(sc, IPW2100_INFO_CURRENT_BSSID,
16853847Seh146360 		    macaddr, &len);
16863847Seh146360 
16873847Seh146360 		in = ieee80211_find_node(&ic->ic_scan, macaddr);
16883847Seh146360 		if (in == NULL)
16893847Seh146360 			break;
16903847Seh146360 
16913847Seh146360 		(void) ieee80211_sta_join(ic, in);
16923847Seh146360 		ieee80211_node_authorize(in);
16933847Seh146360 
16943847Seh146360 		/*
16953847Seh146360 		 * We can send data now; update the fastpath with our
16963847Seh146360 		 * current associated BSSID.
16973847Seh146360 		 */
16983847Seh146360 		if (ic->ic_flags & IEEE80211_F_PRIVACY)
16993847Seh146360 			wd.wd_secalloc = WIFI_SEC_WEP;
17003847Seh146360 		else
17013847Seh146360 			wd.wd_secalloc = WIFI_SEC_NONE;
17023847Seh146360 		wd.wd_opmode = ic->ic_opmode;
17033847Seh146360 		IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
17043847Seh146360 		(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
17053847Seh146360 
17063847Seh146360 		break;
17073847Seh146360 
17083847Seh146360 	case IEEE80211_S_INIT:
17093847Seh146360 	case IEEE80211_S_SCAN:
17103847Seh146360 	case IEEE80211_S_AUTH:
17113847Seh146360 	case IEEE80211_S_ASSOC:
17123847Seh146360 		break;
17133847Seh146360 	}
17143847Seh146360 
17153847Seh146360 	/*
17163847Seh146360 	 * notify to update the link
17173847Seh146360 	 */
17183847Seh146360 	if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) {
17193847Seh146360 		/*
17203847Seh146360 		 * previously disconnected and now connected
17213847Seh146360 		 */
17223847Seh146360 		sc->sc_linkstate = LINK_STATE_UP;
17233847Seh146360 		sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE;
17243847Seh146360 	} else if ((ic->ic_state == IEEE80211_S_RUN) &&
17253847Seh146360 	    (state != IEEE80211_S_RUN)) {
17263847Seh146360 		/*
17273847Seh146360 		 * previously connected andd now disconnected
17283847Seh146360 		 */
17293847Seh146360 		sc->sc_linkstate = LINK_STATE_DOWN;
17303847Seh146360 		sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE;
17313847Seh146360 	}
17323847Seh146360 
17333847Seh146360 	ic->ic_state = state;
17343847Seh146360 	return (DDI_SUCCESS);
17353847Seh146360 }
17363847Seh146360 
17373847Seh146360 /*
17383847Seh146360  * GLD operations
17393847Seh146360  */
17403847Seh146360 /* ARGSUSED */
17413847Seh146360 static int
ipw2100_m_stat(void * arg,uint_t stat,uint64_t * val)17423847Seh146360 ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val)
17433847Seh146360 {
17443847Seh146360 	ieee80211com_t	*ic = (ieee80211com_t *)arg;
17453847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip,
17464784Seh146360 	    CE_CONT,
17474784Seh146360 	    "ipw2100_m_stat(): enter\n"));
17483847Seh146360 	/*
17493847Seh146360 	 * some of below statistic data are from hardware, some from net80211
17503847Seh146360 	 */
17513847Seh146360 	switch (stat) {
17523847Seh146360 	case MAC_STAT_RBYTES:
17533847Seh146360 		*val = ic->ic_stats.is_rx_bytes;
17543847Seh146360 		break;
17553847Seh146360 	case MAC_STAT_IPACKETS:
17563847Seh146360 		*val = ic->ic_stats.is_rx_frags;
17573847Seh146360 		break;
17583847Seh146360 	case MAC_STAT_OBYTES:
17593847Seh146360 		*val = ic->ic_stats.is_tx_bytes;
17603847Seh146360 		break;
17613847Seh146360 	case MAC_STAT_OPACKETS:
17623847Seh146360 		*val = ic->ic_stats.is_tx_frags;
17633847Seh146360 		break;
17643847Seh146360 	/*
17653847Seh146360 	 * Get below from hardware statistic, retrieve net80211 value once 1s
17663847Seh146360 	 */
17673847Seh146360 	case WIFI_STAT_TX_FRAGS:
17683847Seh146360 	case WIFI_STAT_MCAST_TX:
17693847Seh146360 	case WIFI_STAT_TX_FAILED:
17703847Seh146360 	case WIFI_STAT_TX_RETRANS:
17713847Seh146360 	case WIFI_STAT_RTS_SUCCESS:
17723847Seh146360 	case WIFI_STAT_ACK_FAILURE:
17733847Seh146360 	case WIFI_STAT_RX_FRAGS:
17743847Seh146360 	case WIFI_STAT_MCAST_RX:
17753847Seh146360 	/*
17763847Seh146360 	 * Get blow information from net80211
17773847Seh146360 	 */
17783847Seh146360 	case WIFI_STAT_RTS_FAILURE:
17793847Seh146360 	case WIFI_STAT_RX_DUPS:
17803847Seh146360 	case WIFI_STAT_FCS_ERRORS:
17813847Seh146360 	case WIFI_STAT_WEP_ERRORS:
17823847Seh146360 		return (ieee80211_stat(ic, stat, val));
17833847Seh146360 	/*
17843847Seh146360 	 * need be supported in the future
17853847Seh146360 	 */
17863847Seh146360 	case MAC_STAT_IFSPEED:
17873847Seh146360 	case MAC_STAT_NOXMTBUF:
17883847Seh146360 	case MAC_STAT_IERRORS:
17893847Seh146360 	case MAC_STAT_OERRORS:
17903847Seh146360 	default:
17913847Seh146360 		return (ENOTSUP);
17923847Seh146360 	}
17933847Seh146360 	return (0);
17943847Seh146360 }
17953847Seh146360 
17963847Seh146360 /* ARGSUSED */
17973847Seh146360 static int
ipw2100_m_multicst(void * arg,boolean_t add,const uint8_t * mca)17983847Seh146360 ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
17993847Seh146360 {
18003847Seh146360 	/* not supported */
18013847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip,
18024784Seh146360 	    CE_CONT,
18034784Seh146360 	    "ipw2100_m_multicst(): enter\n"));
18043847Seh146360 
18057194Seh146360 	return (0);
18063847Seh146360 }
18073847Seh146360 
18083847Seh146360 /*
18093847Seh146360  * This thread function is used to handle the fatal error.
18103847Seh146360  */
18113847Seh146360 static void
ipw2100_thread(struct ipw2100_softc * sc)18123847Seh146360 ipw2100_thread(struct ipw2100_softc *sc)
18133847Seh146360 {
18143847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
18153847Seh146360 	int32_t			nlstate;
18163847Seh146360 	int			stat_cnt = 0;
18173847Seh146360 
18183847Seh146360 	IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
18193847Seh146360 	    "ipw2100_thread(): into ipw2100 thread--> %d\n",
18203847Seh146360 	    sc->sc_linkstate));
18213847Seh146360 
18223847Seh146360 	mutex_enter(&sc->sc_mflock);
18233847Seh146360 
18243847Seh146360 	while (sc->sc_mfthread_switch) {
18253847Seh146360 		/*
18263847Seh146360 		 * notify the link state
18273847Seh146360 		 */
18283847Seh146360 		if (ic->ic_mach && (sc->sc_flags & IPW2100_FLAG_LINK_CHANGE)) {
18293847Seh146360 			IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
18303847Seh146360 			    "ipw2100_thread(): link status --> %d\n",
18313847Seh146360 			    sc->sc_linkstate));
18323847Seh146360 
18333847Seh146360 			sc->sc_flags &= ~IPW2100_FLAG_LINK_CHANGE;
18343847Seh146360 			nlstate = sc->sc_linkstate;
18353847Seh146360 
18363847Seh146360 			mutex_exit(&sc->sc_mflock);
18373847Seh146360 			mac_link_update(ic->ic_mach, nlstate);
18383847Seh146360 			mutex_enter(&sc->sc_mflock);
18393847Seh146360 		}
18403847Seh146360 
18413847Seh146360 		/*
18423847Seh146360 		 * recovery interrupt fatal error
18433847Seh146360 		 */
18443847Seh146360 		if (ic->ic_mach &&
18453847Seh146360 		    (sc->sc_flags & IPW2100_FLAG_HW_ERR_RECOVER)) {
18463847Seh146360 
18473847Seh146360 			IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT,
18483847Seh146360 			    "try to recover fatal hw error\n"));
18493847Seh146360 			sc->sc_flags &= ~IPW2100_FLAG_HW_ERR_RECOVER;
18503847Seh146360 
18513847Seh146360 			mutex_exit(&sc->sc_mflock);
18523847Seh146360 			(void) ipw2100_init(sc); /* Force stat machine */
18533847Seh146360 			delay(drv_usectohz(delay_fatal_recover));
18543847Seh146360 			mutex_enter(&sc->sc_mflock);
18553847Seh146360 		}
18563847Seh146360 
18573847Seh146360 		/*
18583847Seh146360 		 * get statistic, the value will be retrieved by m_stat
18593847Seh146360 		 */
18603847Seh146360 		if (stat_cnt == 10) {
18613847Seh146360 			stat_cnt = 0; /* re-start */
18623847Seh146360 
18633847Seh146360 			mutex_exit(&sc->sc_mflock);
18643847Seh146360 			ipw2100_get_statistics(sc);
18653847Seh146360 			mutex_enter(&sc->sc_mflock);
18663847Seh146360 		} else
18673847Seh146360 			stat_cnt++; /* until 1s */
18683847Seh146360 
18693847Seh146360 		mutex_exit(&sc->sc_mflock);
18703847Seh146360 		delay(drv_usectohz(delay_aux_thread));
18713847Seh146360 		mutex_enter(&sc->sc_mflock);
18723847Seh146360 	}
18733847Seh146360 	sc->sc_mf_thread = NULL;
18743847Seh146360 	cv_broadcast(&sc->sc_mfthread_cv);
18753847Seh146360 	mutex_exit(&sc->sc_mflock);
18763847Seh146360 }
18773847Seh146360 
18783847Seh146360 static int
ipw2100_m_start(void * arg)18793847Seh146360 ipw2100_m_start(void *arg)
18803847Seh146360 {
18813847Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
18823847Seh146360 
18833847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
18843847Seh146360 	    "ipw2100_m_start(): enter\n"));
18853847Seh146360 
18863847Seh146360 	/*
18873847Seh146360 	 * initialize ipw2100 hardware
18883847Seh146360 	 */
18893847Seh146360 	(void) ipw2100_init(sc);
18903847Seh146360 
18913847Seh146360 	sc->sc_flags |= IPW2100_FLAG_RUNNING;
18923847Seh146360 	/*
18933847Seh146360 	 * fix KCF bug. - workaround, need to fix it in net80211
18943847Seh146360 	 */
18953847Seh146360 	(void) crypto_mech2id(SUN_CKM_RC4);
18963847Seh146360 
18977194Seh146360 	return (0);
18983847Seh146360 }
18993847Seh146360 
19003847Seh146360 static void
ipw2100_m_stop(void * arg)19013847Seh146360 ipw2100_m_stop(void *arg)
19023847Seh146360 {
19033847Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
19043847Seh146360 
19053847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
19063847Seh146360 	    "ipw2100_m_stop(): enter\n"));
19073847Seh146360 
19083847Seh146360 	ipw2100_stop(sc);
19093847Seh146360 
19103847Seh146360 	sc->sc_flags &= ~IPW2100_FLAG_RUNNING;
19113847Seh146360 }
19123847Seh146360 
19133847Seh146360 static int
ipw2100_m_unicst(void * arg,const uint8_t * macaddr)19143847Seh146360 ipw2100_m_unicst(void *arg, const uint8_t *macaddr)
19153847Seh146360 {
19163847Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
19173847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
19183847Seh146360 	int			err;
19193847Seh146360 
19203847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
19213847Seh146360 	    "ipw2100_m_unicst(): enter\n"));
19223847Seh146360 
19233847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
19243847Seh146360 	    "ipw2100_m_unicst(): GLD setting MAC address to "
19253847Seh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
19263847Seh146360 	    macaddr[0], macaddr[1], macaddr[2],
19273847Seh146360 	    macaddr[3], macaddr[4], macaddr[5]));
19283847Seh146360 
19293847Seh146360 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
19303847Seh146360 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
19313847Seh146360 
19323847Seh146360 		if (sc->sc_flags & IPW2100_FLAG_RUNNING) {
19333847Seh146360 			err = ipw2100_config(sc);
19343847Seh146360 			if (err != DDI_SUCCESS) {
19353847Seh146360 				IPW2100_WARN((sc->sc_dip, CE_WARN,
19363847Seh146360 				    "ipw2100_m_unicst(): "
19373847Seh146360 				    "device configuration failed\n"));
19383847Seh146360 				goto fail;
19393847Seh146360 			}
19403847Seh146360 		}
19413847Seh146360 	}
19423847Seh146360 
19437194Seh146360 	return (0);
19443847Seh146360 fail:
19457194Seh146360 	return (EIO);
19463847Seh146360 }
19473847Seh146360 
19483847Seh146360 static int
ipw2100_m_promisc(void * arg,boolean_t on)19493847Seh146360 ipw2100_m_promisc(void *arg, boolean_t on)
19503847Seh146360 {
19513847Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
19523847Seh146360 	int recfg, err;
19533847Seh146360 
19543847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
19553847Seh146360 	    "ipw2100_m_promisc(): enter. "
19563847Seh146360 	    "GLD setting promiscuous mode - %d\n", on));
19573847Seh146360 
19583847Seh146360 	recfg = 0;
19593847Seh146360 	if (on)
19603847Seh146360 		if (!(sc->if_flags & IFF_PROMISC)) {
19613847Seh146360 			sc->if_flags |= IFF_PROMISC;
19623847Seh146360 			recfg = 1;
19633847Seh146360 		}
19643847Seh146360 	else
19653847Seh146360 		if (sc->if_flags & IFF_PROMISC) {
19663847Seh146360 			sc->if_flags &= ~IFF_PROMISC;
19673847Seh146360 			recfg = 1;
19683847Seh146360 		}
19693847Seh146360 
19703847Seh146360 	if (recfg && (sc->sc_flags & IPW2100_FLAG_RUNNING)) {
19713847Seh146360 		err = ipw2100_config(sc);
19723847Seh146360 		if (err != DDI_SUCCESS) {
19733847Seh146360 			IPW2100_WARN((sc->sc_dip, CE_WARN,
19743847Seh146360 			    "ipw2100_m_promisc(): "
19753847Seh146360 			    "device configuration failed\n"));
19763847Seh146360 			goto fail;
19773847Seh146360 		}
19783847Seh146360 	}
19793847Seh146360 
19807194Seh146360 	return (0);
19813847Seh146360 fail:
19827194Seh146360 	return (EIO);
19833847Seh146360 }
19843847Seh146360 
19853847Seh146360 static mblk_t *
ipw2100_m_tx(void * arg,mblk_t * mp)19863847Seh146360 ipw2100_m_tx(void *arg, mblk_t *mp)
19873847Seh146360 {
19883847Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
19893847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
19903847Seh146360 	mblk_t			*next;
19913847Seh146360 
19923847Seh146360 	/*
19933847Seh146360 	 * No data frames go out unless we're associated; this
19943847Seh146360 	 * should not happen as the 802.11 layer does not enable
19953847Seh146360 	 * the xmit queue until we enter the RUN state.
19963847Seh146360 	 */
19973847Seh146360 	if (ic->ic_state != IEEE80211_S_RUN) {
19983847Seh146360 		IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
19993847Seh146360 		    "ipw2100_m_tx(): discard msg, ic_state = %u\n",
20003847Seh146360 		    ic->ic_state));
20013847Seh146360 		freemsgchain(mp);
20023847Seh146360 		return (NULL);
20033847Seh146360 	}
20043847Seh146360 
20053847Seh146360 	while (mp != NULL) {
20063847Seh146360 		next = mp->b_next;
20073847Seh146360 		mp->b_next = NULL;
20083847Seh146360 		if (ipw2100_send(ic, mp, IEEE80211_FC0_TYPE_DATA) !=
20093847Seh146360 		    DDI_SUCCESS) {
20103847Seh146360 			mp->b_next = next;
20113847Seh146360 			break;
20123847Seh146360 		}
20133847Seh146360 		mp = next;
20143847Seh146360 	}
20153847Seh146360 	return (mp);
20163847Seh146360 }
20173847Seh146360 
20183847Seh146360 /* ARGSUSED */
20193847Seh146360 static int
ipw2100_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)20203847Seh146360 ipw2100_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
20213847Seh146360 {
20223847Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)ic;
20233847Seh146360 	struct ieee80211_node	*in;
20243847Seh146360 	struct ieee80211_frame	wh, *wh_tmp;
20253847Seh146360 	struct ieee80211_key	*k;
20263847Seh146360 	uint8_t			*hdat;
20273847Seh146360 	mblk_t			*m0, *m;
20283847Seh146360 	size_t			cnt, off;
20293847Seh146360 	struct ipw2100_bd	*txbd[2];
20303847Seh146360 	struct ipw2100_txb	*txbuf;
20313847Seh146360 	struct dma_region	*dr;
20323847Seh146360 	struct ipw2100_hdr	*h;
20333847Seh146360 	uint32_t		idx, bidx;
20343847Seh146360 	int			err;
20353847Seh146360 
20363847Seh146360 	ASSERT(mp->b_next == NULL);
20373847Seh146360 
20383847Seh146360 	m0 = NULL;
20393847Seh146360 	m = NULL;
20403847Seh146360 	err = DDI_SUCCESS;
20413847Seh146360 
20423847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
20433847Seh146360 	    "ipw2100_send(): enter\n"));
20443847Seh146360 
20453847Seh146360 	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) {
20463847Seh146360 		/*
20473847Seh146360 		 * it is impossible to send non-data 802.11 frame in current
20483847Seh146360 		 * ipw driver. Therefore, drop the package
20493847Seh146360 		 */
20503847Seh146360 		freemsg(mp);
20513847Seh146360 		err = DDI_SUCCESS;
20523847Seh146360 		goto fail0;
20533847Seh146360 	}
20543847Seh146360 
20553847Seh146360 	mutex_enter(&sc->sc_tx_lock);
20563847Seh146360 
20573847Seh146360 	/*
20583847Seh146360 	 * need 2 descriptors: 1 for SEND cmd parameter header,
20593847Seh146360 	 * and the other for payload, i.e., 802.11 frame including 802.11
20603847Seh146360 	 * frame header
20613847Seh146360 	 */
20623847Seh146360 	if (sc->sc_tx_free < 2) {
20633847Seh146360 		mutex_enter(&sc->sc_resched_lock);
20643847Seh146360 		IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_WARN,
20653847Seh146360 		    "ipw2100_send(): no enough descriptors(%d)\n",
20663847Seh146360 		    sc->sc_tx_free));
20673847Seh146360 		ic->ic_stats.is_tx_nobuf++; /* no enough buffer */
20683847Seh146360 		sc->sc_flags |= IPW2100_FLAG_TX_SCHED;
20693847Seh146360 		err = DDI_FAILURE;
20703847Seh146360 		mutex_exit(&sc->sc_resched_lock);
20713847Seh146360 		goto fail1;
20723847Seh146360 	}
20733847Seh146360 	IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT,
20743847Seh146360 	    "ipw2100_send(): tx-free=%d,tx-curr=%d\n",
20753847Seh146360 	    sc->sc_tx_free, sc->sc_tx_cur));
20763847Seh146360 
20773847Seh146360 	wh_tmp = (struct ieee80211_frame *)mp->b_rptr;
20783847Seh146360 	in = ieee80211_find_txnode(ic, wh_tmp->i_addr1);
20793847Seh146360 	if (in == NULL) { /* can not find tx node, drop the package */
20803847Seh146360 		freemsg(mp);
20813847Seh146360 		err = DDI_SUCCESS;
20823847Seh146360 		goto fail1;
20833847Seh146360 	}
20843847Seh146360 	in->in_inact = 0;
20853847Seh146360 	(void) ieee80211_encap(ic, mp, in);
20863847Seh146360 	ieee80211_free_node(in);
20873847Seh146360 
20883847Seh146360 	if (wh_tmp->i_fc[1] & IEEE80211_FC1_WEP) {
20893847Seh146360 		/*
20903847Seh146360 		 * it is very bad that ieee80211_crypto_encap can only accept a
20913847Seh146360 		 * single continuous buffer.
20923847Seh146360 		 */
20933847Seh146360 		/*
20943847Seh146360 		 * allocate 32 more bytes is to be compatible with further
20953847Seh146360 		 * ieee802.11i standard.
20963847Seh146360 		 */
20973847Seh146360 		m = allocb(msgdsize(mp) + 32, BPRI_MED);
20983847Seh146360 		if (m == NULL) { /* can not alloc buf, drop this package */
20993847Seh146360 			IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
21003847Seh146360 			    "ipw2100_send(): msg allocation failed\n"));
21013847Seh146360 
21023847Seh146360 			freemsg(mp);
21033847Seh146360 
21043847Seh146360 			err = DDI_SUCCESS;
21053847Seh146360 			goto fail1;
21063847Seh146360 		}
21073847Seh146360 		off = 0;
21083847Seh146360 		m0 = mp;
21093847Seh146360 		while (m0) {
21103847Seh146360 			cnt = MBLKL(m0);
21113847Seh146360 			if (cnt) {
21123847Seh146360 				(void) memcpy(m->b_rptr + off, m0->b_rptr, cnt);
21133847Seh146360 				off += cnt;
21143847Seh146360 			}
21153847Seh146360 			m0 = m0->b_cont;
21163847Seh146360 		}
21173847Seh146360 		m->b_wptr += off;
21183847Seh146360 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
21193847Seh146360 		    "ipw2100_send(): "
21203847Seh146360 		    "Encrypting 802.11 frame started, %d, %d\n",
21213847Seh146360 		    msgdsize(mp), MBLKL(mp)));
21223847Seh146360 		k = ieee80211_crypto_encap(ic, m);
21233847Seh146360 		if (k == NULL) { /* can not get the key, drop packages */
21243847Seh146360 			IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
21253847Seh146360 			    "ipw2100_send(): "
21263847Seh146360 			    "Encrypting 802.11 frame failed\n"));
21273847Seh146360 
21283847Seh146360 			freemsg(mp);
21293847Seh146360 			err = DDI_SUCCESS;
21303847Seh146360 			goto fail2;
21313847Seh146360 		}
21323847Seh146360 		IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT,
21333847Seh146360 		    "ipw2100_send(): "
21343847Seh146360 		    "Encrypting 802.11 frame finished, %d, %d, k=0x%08x\n",
21353847Seh146360 		    msgdsize(mp), MBLKL(mp), k->wk_flags));
21363847Seh146360 	}
21373847Seh146360 
21383847Seh146360 	/*
21393847Seh146360 	 * header descriptor
21403847Seh146360 	 */
21413847Seh146360 	idx = sc->sc_tx_cur;
21423847Seh146360 	txbd[0]  = &sc->sc_txbd[idx];
21433847Seh146360 	if ((idx & 1) == 0)
21443847Seh146360 		bidx = idx / 2;
21453847Seh146360 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD);
21463847Seh146360 	sc->sc_tx_free--;
21473847Seh146360 
21483847Seh146360 	/*
21493847Seh146360 	 * payload descriptor
21503847Seh146360 	 */
21513847Seh146360 	idx = sc->sc_tx_cur;
21523847Seh146360 	txbd[1]  = &sc->sc_txbd[idx];
21533847Seh146360 	if ((idx & 1) == 0)
21543847Seh146360 		bidx = idx / 2;
21553847Seh146360 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD);
21563847Seh146360 	sc->sc_tx_free--;
21573847Seh146360 
21583847Seh146360 	/*
21593847Seh146360 	 * one buffer, SEND cmd header and payload buffer
21603847Seh146360 	 */
21613847Seh146360 	txbuf = sc->sc_txbufs[bidx];
21623847Seh146360 	dr = &sc->sc_dma_txbufs[bidx];
21633847Seh146360 
21643847Seh146360 	/*
21653847Seh146360 	 * extract 802.11 header from message, fill wh from m0
21663847Seh146360 	 */
21673847Seh146360 	hdat = (uint8_t *)&wh;
21683847Seh146360 	off = 0;
21693847Seh146360 	if (m)
21703847Seh146360 		m0 = m;
21713847Seh146360 	else
21723847Seh146360 		m0 = mp;
21733847Seh146360 	while (off < sizeof (wh)) {
21743847Seh146360 		cnt = MBLKL(m0);
21753847Seh146360 		if (cnt > (sizeof (wh) - off))
21763847Seh146360 			cnt = sizeof (wh) - off;
21773847Seh146360 		if (cnt) {
21783847Seh146360 			(void) memcpy(hdat + off, m0->b_rptr, cnt);
21793847Seh146360 			off += cnt;
21803847Seh146360 			m0->b_rptr += cnt;
21813847Seh146360 		}
21823847Seh146360 		else
21833847Seh146360 			m0 = m0->b_cont;
21843847Seh146360 	}
21853847Seh146360 
21863847Seh146360 	/*
21873847Seh146360 	 * prepare SEND cmd header
21883847Seh146360 	 */
21893847Seh146360 	h		= &txbuf->txb_hdr;
21903847Seh146360 	h->type		= LE_32(IPW2100_CMD_SEND);
21913847Seh146360 	h->subtype	= LE_32(0);
21923847Seh146360 	h->encrypted    = ic->ic_flags & IEEE80211_F_PRIVACY ? 1 : 0;
21933847Seh146360 	h->encrypt	= 0;
21943847Seh146360 	h->keyidx	= 0;
21953847Seh146360 	h->keysz	= 0;
21963847Seh146360 	h->fragsz	= LE_16(0);
21973847Seh146360 	IEEE80211_ADDR_COPY(h->saddr, wh.i_addr2);
21983847Seh146360 	if (ic->ic_opmode == IEEE80211_M_STA)
21993847Seh146360 		IEEE80211_ADDR_COPY(h->daddr, wh.i_addr3);
22003847Seh146360 	else
22013847Seh146360 		IEEE80211_ADDR_COPY(h->daddr, wh.i_addr1);
22023847Seh146360 
22033847Seh146360 	/*
22043847Seh146360 	 * extract payload from message into tx data buffer
22053847Seh146360 	 */
22063847Seh146360 	off = 0;
22073847Seh146360 	while (m0) {
22083847Seh146360 		cnt = MBLKL(m0);
22093847Seh146360 		if (cnt) {
22103847Seh146360 			(void) memcpy(&txbuf->txb_dat[off], m0->b_rptr, cnt);
22113847Seh146360 			off += cnt;
22123847Seh146360 		}
22133847Seh146360 		m0 = m0->b_cont;
22143847Seh146360 	}
22153847Seh146360 
22163847Seh146360 	/*
22173847Seh146360 	 * fill SEND cmd header descriptor
22183847Seh146360 	 */
22193847Seh146360 	txbd[0]->phyaddr = LE_32(dr->dr_pbase +
22203847Seh146360 	    OFFSETOF(struct ipw2100_txb, txb_hdr));
22213847Seh146360 	txbd[0]->len	= LE_32(sizeof (struct ipw2100_hdr));
22223847Seh146360 	txbd[0]->flags	= IPW2100_BD_FLAG_TX_FRAME_802_3 |
22233847Seh146360 	    IPW2100_BD_FLAG_TX_NOT_LAST_FRAGMENT;
22243847Seh146360 	txbd[0]->nfrag	= 2;
22253847Seh146360 	/*
22263847Seh146360 	 * fill payload descriptor
22273847Seh146360 	 */
22283847Seh146360 	txbd[1]->phyaddr = LE_32(dr->dr_pbase +
22293847Seh146360 	    OFFSETOF(struct ipw2100_txb, txb_dat[0]));
22303847Seh146360 	txbd[1]->len	= LE_32(off);
22313847Seh146360 	txbd[1]->flags	= IPW2100_BD_FLAG_TX_FRAME_802_3 |
22323847Seh146360 	    IPW2100_BD_FLAG_TX_LAST_FRAGMENT;
22333847Seh146360 	txbd[1]->nfrag	= 0;
22343847Seh146360 
22353847Seh146360 	/*
22363847Seh146360 	 * dma sync
22373847Seh146360 	 */
22383847Seh146360 	(void) ddi_dma_sync(dr->dr_hnd, 0, sizeof (struct ipw2100_txb),
22393847Seh146360 	    DDI_DMA_SYNC_FORDEV);
22403847Seh146360 	(void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd,
22413847Seh146360 	    (txbd[0] - sc->sc_txbd) * sizeof (struct ipw2100_bd),
22423847Seh146360 	    sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
22433847Seh146360 	/*
22443847Seh146360 	 * since txbd[1] may not be successive to txbd[0] due to the ring
22453847Seh146360 	 * organization, another dma_sync is needed to simplify the logic
22463847Seh146360 	 */
22473847Seh146360 	(void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd,
22483847Seh146360 	    (txbd[1] - sc->sc_txbd) * sizeof (struct ipw2100_bd),
22493847Seh146360 	    sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV);
22503847Seh146360 	/*
22513847Seh146360 	 * update txcur
22523847Seh146360 	 */
22533847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur);
22543847Seh146360 
22553847Seh146360 	if (mp) /* success, free the original message */
22563847Seh146360 		freemsg(mp);
22573847Seh146360 fail2:
22583847Seh146360 	if (m)
22593847Seh146360 		freemsg(m);
22603847Seh146360 fail1:
22613847Seh146360 	mutex_exit(&sc->sc_tx_lock);
22623847Seh146360 fail0:
22633847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
22643847Seh146360 	    "ipw2100_send(): exit - err=%d\n", err));
22653847Seh146360 
22663847Seh146360 	return (err);
22673847Seh146360 }
22683847Seh146360 
22693847Seh146360 /*
22703847Seh146360  * IOCTL Handler
22713847Seh146360  */
22723847Seh146360 #define	IEEE80211_IOCTL_REQUIRED	(1)
22733847Seh146360 #define	IEEE80211_IOCTL_NOT_REQUIRED	(0)
22743847Seh146360 static void
ipw2100_m_ioctl(void * arg,queue_t * q,mblk_t * m)22753847Seh146360 ipw2100_m_ioctl(void *arg, queue_t *q, mblk_t *m)
22763847Seh146360 {
22773847Seh146360 	struct ipw2100_softc	*sc  = (struct ipw2100_softc *)arg;
22783847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
22793847Seh146360 	int			err;
22803847Seh146360 
22813847Seh146360 	IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT,
22823847Seh146360 	    "ipw2100_m_ioctl(): enter\n"));
22833847Seh146360 
22843847Seh146360 	/*
22853847Seh146360 	 * check whether or not need to handle this in net80211
22863847Seh146360 	 */
22873847Seh146360 	if (ipw2100_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED)
22883847Seh146360 		return; /* succes or fail */
22893847Seh146360 
22903847Seh146360 	err = ieee80211_ioctl(ic, q, m);
22913847Seh146360 	if (err == ENETRESET) {
22923847Seh146360 		if (sc->sc_flags & IPW2100_FLAG_RUNNING) {
22933847Seh146360 			(void) ipw2100_m_start(sc);
22943847Seh146360 			(void) ieee80211_new_state(ic,
22953847Seh146360 			    IEEE80211_S_SCAN, -1);
22963847Seh146360 		}
22973847Seh146360 	}
22983847Seh146360 	if (err == ERESTART) {
22993847Seh146360 		if (sc->sc_flags & IPW2100_FLAG_RUNNING)
23003847Seh146360 			(void) ipw2100_chip_reset(sc);
23013847Seh146360 	}
23023847Seh146360 }
23033847Seh146360 
23043847Seh146360 static int
ipw2100_ioctl(struct ipw2100_softc * sc,queue_t * q,mblk_t * m)23053847Seh146360 ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m)
23063847Seh146360 {
23073847Seh146360 	struct iocblk	*iocp;
23083847Seh146360 	uint32_t	len, ret, cmd;
23093847Seh146360 	mblk_t		*m0;
23103847Seh146360 	boolean_t	need_privilege;
23113847Seh146360 	boolean_t	need_net80211;
23123847Seh146360 
23133847Seh146360 	if (MBLKL(m) < sizeof (struct iocblk)) {
23143847Seh146360 		IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
23153847Seh146360 		    "ipw2100_ioctl(): ioctl buffer too short, %u\n",
23163847Seh146360 		    MBLKL(m)));
23173847Seh146360 		miocnak(q, m, 0, EINVAL);
23183847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
23193847Seh146360 	}
23203847Seh146360 
23213847Seh146360 	/*
23223847Seh146360 	 * Validate the command
23233847Seh146360 	 */
23247194Seh146360 	iocp = (struct iocblk *)(uintptr_t)m->b_rptr;
23253847Seh146360 	iocp->ioc_error = 0;
23263847Seh146360 	cmd = iocp->ioc_cmd;
23273847Seh146360 	need_privilege = B_TRUE;
23283847Seh146360 	switch (cmd) {
23293847Seh146360 	case WLAN_SET_PARAM:
23303847Seh146360 	case WLAN_COMMAND:
23313847Seh146360 		break;
23323847Seh146360 	case WLAN_GET_PARAM:
23333847Seh146360 		need_privilege = B_FALSE;
23343847Seh146360 		break;
23353847Seh146360 	default:
23363847Seh146360 		IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
23373847Seh146360 		    "ieee80211_ioctl(): unknown cmd 0x%x", cmd));
23383847Seh146360 		miocnak(q, m, 0, EINVAL);
23393847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
23403847Seh146360 	}
23413847Seh146360 
23427408SSebastien.Roy@Sun.COM 	if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) {
23437408SSebastien.Roy@Sun.COM 		miocnak(q, m, 0, ret);
23447408SSebastien.Roy@Sun.COM 		return (IEEE80211_IOCTL_NOT_REQUIRED);
23453847Seh146360 	}
23467408SSebastien.Roy@Sun.COM 
23473847Seh146360 	/*
23483847Seh146360 	 * sanity check
23493847Seh146360 	 */
23503847Seh146360 	m0 = m->b_cont;
23513847Seh146360 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
23523847Seh146360 	    m0 == NULL) {
23533847Seh146360 		miocnak(q, m, 0, EINVAL);
23543847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
23553847Seh146360 	}
23563847Seh146360 	/*
23573847Seh146360 	 * assuming single data block
23583847Seh146360 	 */
23593847Seh146360 	if (m0->b_cont) {
23603847Seh146360 		freemsg(m0->b_cont);
23613847Seh146360 		m0->b_cont = NULL;
23623847Seh146360 	}
23633847Seh146360 
23643847Seh146360 	need_net80211 = B_FALSE;
23653847Seh146360 	ret = ipw2100_getset(sc, m0, cmd, &need_net80211);
23663847Seh146360 	if (!need_net80211) {
23673847Seh146360 		len = msgdsize(m0);
23683847Seh146360 
23693847Seh146360 		IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
23703847Seh146360 		    "ipw2100_ioctl(): go to call miocack with "
23713847Seh146360 		    "ret = %d, len = %d\n", ret, len));
23723847Seh146360 		miocack(q, m, len, ret);
23733847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
23743847Seh146360 	}
23753847Seh146360 
23763847Seh146360 	/*
23773847Seh146360 	 * IEEE80211_IOCTL_REQUIRED - need net80211 handle
23783847Seh146360 	 */
23793847Seh146360 	return (IEEE80211_IOCTL_REQUIRED);
23803847Seh146360 }
23813847Seh146360 
23823847Seh146360 static int
ipw2100_getset(struct ipw2100_softc * sc,mblk_t * m,uint32_t cmd,boolean_t * need_net80211)23833847Seh146360 ipw2100_getset(struct ipw2100_softc *sc, mblk_t *m, uint32_t cmd,
23843847Seh146360 	boolean_t *need_net80211)
23853847Seh146360 {
23863847Seh146360 	wldp_t		*infp, *outfp;
23873847Seh146360 	uint32_t	id;
23883847Seh146360 	int		ret; /* IEEE80211_IOCTL - handled by net80211 */
23893847Seh146360 
23907194Seh146360 	infp  = (wldp_t *)(uintptr_t)m->b_rptr;
23917194Seh146360 	outfp = (wldp_t *)(uintptr_t)m->b_rptr;
23923847Seh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
23933847Seh146360 
23943847Seh146360 	id = infp->wldp_id;
23953847Seh146360 	IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT,
23963847Seh146360 	    "ipw2100_getset(): id = 0x%x\n", id));
23973847Seh146360 	switch (id) {
23983847Seh146360 	/*
23993847Seh146360 	 * which is not supported by net80211, so it
24003847Seh146360 	 * has to be handled from driver side
24013847Seh146360 	 */
24023847Seh146360 	case WL_RADIO:
24033847Seh146360 		ret = ipw_wificfg_radio(sc, cmd, outfp);
24043847Seh146360 		break;
24053847Seh146360 	/*
24063847Seh146360 	 * so far, drier doesn't support fix-rates
24073847Seh146360 	 */
24083847Seh146360 	case WL_DESIRED_RATES:
24093847Seh146360 		ret = ipw_wificfg_desrates(outfp);
24103847Seh146360 		break;
24113847Seh146360 	/*
24123847Seh146360 	 * current net80211 implementation clears the bssid while
24133847Seh146360 	 * this command received, which will result in the all zero
24143847Seh146360 	 * mac address for scan'ed AP which is just disconnected.
24153847Seh146360 	 * This is a workaround solution until net80211 find a
24163847Seh146360 	 * better method.
24173847Seh146360 	 */
24183847Seh146360 	case WL_DISASSOCIATE:
24193847Seh146360 		ret = ipw_wificfg_disassoc(sc, outfp);
24203847Seh146360 		break;
24213847Seh146360 	default:
24223847Seh146360 		/*
24233847Seh146360 		 * The wifi IOCTL net80211 supported:
24243847Seh146360 		 *	case WL_ESSID:
24253847Seh146360 		 *	case WL_BSSID:
24263847Seh146360 		 *	case WL_WEP_KEY_TAB:
24273847Seh146360 		 *	case WL_WEP_KEY_ID:
24283847Seh146360 		 *	case WL_AUTH_MODE:
24293847Seh146360 		 *	case WL_ENCRYPTION:
24303847Seh146360 		 *	case WL_BSS_TYPE:
24313847Seh146360 		 *	case WL_ESS_LIST:
24323847Seh146360 		 *	case WL_LINKSTATUS:
24333847Seh146360 		 *	case WL_RSSI:
24343847Seh146360 		 *	case WL_SCAN:
24353847Seh146360 		 *	case WL_LOAD_DEFAULTS:
24363847Seh146360 		 */
24375838Seh146360 
24385838Seh146360 		/*
24395838Seh146360 		 * When radio is off, need to ignore all ioctl.  What need to
24405838Seh146360 		 * do is to check radio status firstly.  If radio is ON, pass
24415838Seh146360 		 * it to net80211, otherwise, return to upper layer directly.
24425838Seh146360 		 *
24435838Seh146360 		 * Considering the WL_SUCCESS also means WL_CONNECTED for
24445838Seh146360 		 * checking linkstatus, one exception for WL_LINKSTATUS is to
24455838Seh146360 		 * let net80211 handle it.
24465838Seh146360 		 */
24475838Seh146360 		if ((ipw2100_get_radio(sc) == 0) &&
24485838Seh146360 		    (id != WL_LINKSTATUS)) {
24495838Seh146360 
24505838Seh146360 			IPW2100_REPORT((sc->sc_dip, CE_WARN,
24515838Seh146360 			    "ipw: RADIO is OFF\n"));
24525838Seh146360 
24535838Seh146360 			outfp->wldp_length = WIFI_BUF_OFFSET;
24545838Seh146360 			outfp->wldp_result = WL_SUCCESS;
24555838Seh146360 			ret = 0;
24565838Seh146360 			break;
24575838Seh146360 		}
24585838Seh146360 
24593847Seh146360 		*need_net80211 = B_TRUE; /* let net80211 do the rest */
24603847Seh146360 		return (0);
24613847Seh146360 	}
24623847Seh146360 	/*
24633847Seh146360 	 * we will overwrite everything
24643847Seh146360 	 */
24653847Seh146360 	m->b_wptr = m->b_rptr + outfp->wldp_length;
24663847Seh146360 
24673847Seh146360 	return (ret);
24683847Seh146360 }
24693847Seh146360 
24708367SFei.Feng@Sun.COM /*
24718367SFei.Feng@Sun.COM  * Call back functions for get/set proporty
24728367SFei.Feng@Sun.COM  */
24738367SFei.Feng@Sun.COM 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)24748367SFei.Feng@Sun.COM ipw2100_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2475*11878SVenu.Iyer@Sun.COM     uint_t wldp_length, void *wldp_buf)
24768367SFei.Feng@Sun.COM {
24778367SFei.Feng@Sun.COM 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
24788367SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
24798367SFei.Feng@Sun.COM 	int 			err = 0;
24808367SFei.Feng@Sun.COM 
24818367SFei.Feng@Sun.COM 	switch (wldp_pr_num) {
24828367SFei.Feng@Sun.COM 	/* mac_prop_id */
24838367SFei.Feng@Sun.COM 	case MAC_PROP_WL_DESIRED_RATES:
24848367SFei.Feng@Sun.COM 		IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
24858367SFei.Feng@Sun.COM 		    "ipw2100_m_getprop(): Not Support DESIRED_RATES\n"));
24868367SFei.Feng@Sun.COM 		break;
24878367SFei.Feng@Sun.COM 	case MAC_PROP_WL_RADIO:
24888367SFei.Feng@Sun.COM 		*(wl_linkstatus_t *)wldp_buf = ipw2100_get_radio(sc);
24898367SFei.Feng@Sun.COM 		break;
24908367SFei.Feng@Sun.COM 	default:
24918367SFei.Feng@Sun.COM 		/* go through net80211 */
2492*11878SVenu.Iyer@Sun.COM 		err = ieee80211_getprop(ic, pr_name, wldp_pr_num,
2493*11878SVenu.Iyer@Sun.COM 		    wldp_length, wldp_buf);
24948367SFei.Feng@Sun.COM 		break;
24958367SFei.Feng@Sun.COM 	}
24968367SFei.Feng@Sun.COM 
24978367SFei.Feng@Sun.COM 	return (err);
24988367SFei.Feng@Sun.COM }
24998367SFei.Feng@Sun.COM 
2500*11878SVenu.Iyer@Sun.COM static void
ipw2100_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t prh)2501*11878SVenu.Iyer@Sun.COM ipw2100_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2502*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t prh)
2503*11878SVenu.Iyer@Sun.COM {
2504*11878SVenu.Iyer@Sun.COM 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
2505*11878SVenu.Iyer@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
2506*11878SVenu.Iyer@Sun.COM 
2507*11878SVenu.Iyer@Sun.COM 	ieee80211_propinfo(ic, pr_name, wldp_pr_num, prh);
2508*11878SVenu.Iyer@Sun.COM 
2509*11878SVenu.Iyer@Sun.COM }
2510*11878SVenu.Iyer@Sun.COM 
25118367SFei.Feng@Sun.COM 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)25128367SFei.Feng@Sun.COM ipw2100_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
25138367SFei.Feng@Sun.COM     uint_t wldp_length, const void *wldp_buf)
25148367SFei.Feng@Sun.COM {
25158367SFei.Feng@Sun.COM 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)arg;
25168367SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
25178367SFei.Feng@Sun.COM 	int			err;
25188367SFei.Feng@Sun.COM 
25198367SFei.Feng@Sun.COM 	switch (wldp_pr_num) {
25208367SFei.Feng@Sun.COM 	/* mac_prop_id */
25218367SFei.Feng@Sun.COM 	case MAC_PROP_WL_DESIRED_RATES:
25228367SFei.Feng@Sun.COM 		IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
25238367SFei.Feng@Sun.COM 		    "ipw2100_m_setprop(): Not Support DESIRED_RATES\n"));
25248367SFei.Feng@Sun.COM 		err = ENOTSUP;
25258367SFei.Feng@Sun.COM 		break;
25268367SFei.Feng@Sun.COM 	case MAC_PROP_WL_RADIO:
25278367SFei.Feng@Sun.COM 		IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
25288367SFei.Feng@Sun.COM 		    "ipw2100_m_setprop(): Not Support RADIO\n"));
25298367SFei.Feng@Sun.COM 		err = ENOTSUP;
25308367SFei.Feng@Sun.COM 		break;
25318367SFei.Feng@Sun.COM 	default:
25328367SFei.Feng@Sun.COM 		/* go through net80211 */
25338367SFei.Feng@Sun.COM 		err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
25348367SFei.Feng@Sun.COM 		    wldp_buf);
25358367SFei.Feng@Sun.COM 		break;
25368367SFei.Feng@Sun.COM 	}
25378367SFei.Feng@Sun.COM 
25388367SFei.Feng@Sun.COM 	if (err == ENETRESET) {
25398367SFei.Feng@Sun.COM 		if (sc->sc_flags & IPW2100_FLAG_RUNNING) {
25408367SFei.Feng@Sun.COM 			(void) ipw2100_m_start(sc);
25418367SFei.Feng@Sun.COM 			(void) ieee80211_new_state(ic,
25428367SFei.Feng@Sun.COM 			    IEEE80211_S_SCAN, -1);
25438367SFei.Feng@Sun.COM 		}
25448367SFei.Feng@Sun.COM 
25458367SFei.Feng@Sun.COM 		err = 0;
25468367SFei.Feng@Sun.COM 	}
25478367SFei.Feng@Sun.COM 
25488367SFei.Feng@Sun.COM 	return (err);
25498367SFei.Feng@Sun.COM }
25508367SFei.Feng@Sun.COM 
25513847Seh146360 static int
ipw_wificfg_radio(struct ipw2100_softc * sc,uint32_t cmd,wldp_t * outfp)25523847Seh146360 ipw_wificfg_radio(struct ipw2100_softc *sc, uint32_t cmd, wldp_t *outfp)
25533847Seh146360 {
25543847Seh146360 	uint32_t	ret = ENOTSUP;
25553847Seh146360 
25563847Seh146360 	switch (cmd) {
25573847Seh146360 	case WLAN_GET_PARAM:
25583847Seh146360 		*(wl_linkstatus_t *)(outfp->wldp_buf) = ipw2100_get_radio(sc);
25593847Seh146360 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t);
25603847Seh146360 		outfp->wldp_result = WL_SUCCESS;
25613847Seh146360 		ret = 0; /* command sucess */
25623847Seh146360 		break;
25633847Seh146360 	case WLAN_SET_PARAM:
25643847Seh146360 	default:
25653847Seh146360 		break;
25663847Seh146360 	}
25673847Seh146360 	return (ret);
25683847Seh146360 }
25693847Seh146360 
25703847Seh146360 static int
ipw_wificfg_desrates(wldp_t * outfp)25713847Seh146360 ipw_wificfg_desrates(wldp_t *outfp)
25723847Seh146360 {
25733847Seh146360 	/*
25743847Seh146360 	 * return success, but with result NOTSUPPORTED
25753847Seh146360 	 */
25763847Seh146360 	outfp->wldp_length = WIFI_BUF_OFFSET;
25773847Seh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
25783847Seh146360 	return (0);
25793847Seh146360 }
25803847Seh146360 
25813847Seh146360 static int
ipw_wificfg_disassoc(struct ipw2100_softc * sc,wldp_t * outfp)25823847Seh146360 ipw_wificfg_disassoc(struct ipw2100_softc *sc, wldp_t *outfp)
25833847Seh146360 {
25843847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
25853847Seh146360 
25863847Seh146360 	/*
25873847Seh146360 	 * init the state
25883847Seh146360 	 */
25893847Seh146360 	if (ic->ic_state != IEEE80211_S_INIT) {
25903847Seh146360 		(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
25913847Seh146360 	}
25923847Seh146360 
25933847Seh146360 	/*
25943847Seh146360 	 * return success always
25953847Seh146360 	 */
25963847Seh146360 	outfp->wldp_length = WIFI_BUF_OFFSET;
25973847Seh146360 	outfp->wldp_result = WL_SUCCESS;
25983847Seh146360 	return (0);
25993847Seh146360 }
26003847Seh146360 /* End of IOCTL Handler */
26013847Seh146360 
26023847Seh146360 static void
ipw2100_fix_channel(struct ieee80211com * ic,mblk_t * m)26033847Seh146360 ipw2100_fix_channel(struct ieee80211com *ic, mblk_t *m)
26043847Seh146360 {
26053847Seh146360 	struct ieee80211_frame	*wh;
26063847Seh146360 	uint8_t			subtype;
26073847Seh146360 	uint8_t			*frm, *efrm;
26083847Seh146360 
26093847Seh146360 	wh = (struct ieee80211_frame *)m->b_rptr;
26103847Seh146360 
26113847Seh146360 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
26123847Seh146360 		return;
26133847Seh146360 
26143847Seh146360 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
26153847Seh146360 
26163847Seh146360 	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
26173847Seh146360 	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
26183847Seh146360 		return;
26193847Seh146360 
26203847Seh146360 	/*
26213847Seh146360 	 * assume the message contains only 1 block
26223847Seh146360 	 */
26233847Seh146360 	frm   = (uint8_t *)(wh + 1);
26243847Seh146360 	efrm  = (uint8_t *)m->b_wptr;
26253847Seh146360 	frm  += 12;  /* skip tstamp, bintval and capinfo fields */
26263847Seh146360 	while (frm < efrm) {
26273847Seh146360 		if (*frm == IEEE80211_ELEMID_DSPARMS) {
26283847Seh146360 #if IEEE80211_CHAN_MAX < 255
26294784Seh146360 			if (frm[2] <= IEEE80211_CHAN_MAX)
26303847Seh146360 #endif
26314784Seh146360 			{
26324784Seh146360 				ic->ic_curchan = &ic->ic_sup_channels[frm[2]];
26334784Seh146360 			}
26343847Seh146360 		}
26353847Seh146360 		frm += frm[1] + 2;
26363847Seh146360 	}
26373847Seh146360 }
26383847Seh146360 
26393847Seh146360 static void
ipw2100_rcvpkt(struct ipw2100_softc * sc,struct ipw2100_status * status,uint8_t * rxbuf)26403847Seh146360 ipw2100_rcvpkt(struct ipw2100_softc *sc, struct ipw2100_status *status,
26413847Seh146360     uint8_t *rxbuf)
26423847Seh146360 {
26433847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
26443847Seh146360 	mblk_t			*m;
26453847Seh146360 	struct ieee80211_frame	*wh = (struct ieee80211_frame *)rxbuf;
26463847Seh146360 	struct ieee80211_node	*in;
26473847Seh146360 	uint32_t		rlen;
26483847Seh146360 
26493847Seh146360 	in = ieee80211_find_rxnode(ic, wh);
26503847Seh146360 	rlen = LE_32(status->len);
26513847Seh146360 	m = allocb(rlen, BPRI_MED);
26523847Seh146360 	if (m) {
26533847Seh146360 		(void) memcpy(m->b_wptr, rxbuf, rlen);
26543847Seh146360 		m->b_wptr += rlen;
26553847Seh146360 		if (ic->ic_state == IEEE80211_S_SCAN)
26563847Seh146360 			ipw2100_fix_channel(ic, m);
26573847Seh146360 		(void) ieee80211_input(ic, m, in, status->rssi, 0);
26583847Seh146360 	} else
26593847Seh146360 		IPW2100_WARN((sc->sc_dip, CE_WARN,
26603847Seh146360 		    "ipw2100_rcvpkg(): cannot allocate receive message(%u)\n",
26613847Seh146360 		    LE_32(status->len)));
26623847Seh146360 	ieee80211_free_node(in);
26633847Seh146360 }
26643847Seh146360 
26653847Seh146360 static uint_t
ipw2100_intr(caddr_t arg)26663847Seh146360 ipw2100_intr(caddr_t arg)
26673847Seh146360 {
26687194Seh146360 	struct ipw2100_softc	*sc = (struct ipw2100_softc *)(uintptr_t)arg;
26693847Seh146360 	uint32_t		ireg, ridx, len, i;
26703847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
26713847Seh146360 	struct ipw2100_status	*status;
26723847Seh146360 	uint8_t			*rxbuf;
26733847Seh146360 	struct dma_region	*dr;
26743847Seh146360 	uint32_t		state;
26753847Seh146360 #if DEBUG
26763847Seh146360 	struct ipw2100_bd *rxbd;
26773847Seh146360 #endif
26783847Seh146360 
26798550SSeth.Goldberg@Sun.COM 	if (sc->sc_suspended)
26808550SSeth.Goldberg@Sun.COM 		return (DDI_INTR_UNCLAIMED);
26818550SSeth.Goldberg@Sun.COM 
26823847Seh146360 	ireg = ipw2100_csr_get32(sc, IPW2100_CSR_INTR);
26833847Seh146360 
26843847Seh146360 	if (!(ireg & IPW2100_INTR_MASK_ALL))
26853847Seh146360 		return (DDI_INTR_UNCLAIMED);
26863847Seh146360 
26873847Seh146360 	/*
26883847Seh146360 	 * mask all interrupts
26893847Seh146360 	 */
26903847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0);
26913847Seh146360 
26923847Seh146360 	/*
26933847Seh146360 	 * acknowledge all fired interrupts
26943847Seh146360 	 */
26953847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR, ireg);
26963847Seh146360 
26973847Seh146360 	IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
26983847Seh146360 	    "ipw2100_intr(): interrupt is fired. int=0x%08x\n", ireg));
26993847Seh146360 
27003847Seh146360 	if (ireg & IPW2100_INTR_MASK_ERR) {
27013847Seh146360 
27023847Seh146360 		IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT,
27033847Seh146360 		    "ipw2100_intr(): interrupt is fired, MASK = 0x%08x\n",
27043847Seh146360 		    ireg));
27053847Seh146360 
27063847Seh146360 		/*
27073847Seh146360 		 * inform mfthread to recover hw error
27083847Seh146360 		 */
27093847Seh146360 		mutex_enter(&sc->sc_mflock);
27103847Seh146360 		sc->sc_flags |= IPW2100_FLAG_HW_ERR_RECOVER;
27113847Seh146360 		mutex_exit(&sc->sc_mflock);
27123847Seh146360 
27137194Seh146360 		goto enable_interrupt;
27147194Seh146360 	}
27157194Seh146360 
27167194Seh146360 	/*
27177194Seh146360 	 * FW intr
27187194Seh146360 	 */
27197194Seh146360 	if (ireg & IPW2100_INTR_FW_INIT_DONE) {
27207194Seh146360 		mutex_enter(&sc->sc_ilock);
27217194Seh146360 		sc->sc_flags |= IPW2100_FLAG_FW_INITED;
27227194Seh146360 		cv_signal(&sc->sc_fw_cond);
27237194Seh146360 		mutex_exit(&sc->sc_ilock);
27247194Seh146360 	}
27253847Seh146360 
27267194Seh146360 	/*
27277194Seh146360 	 * RX intr
27287194Seh146360 	 */
27297194Seh146360 	if (ireg & IPW2100_INTR_RX_TRANSFER) {
27307194Seh146360 		ridx = ipw2100_csr_get32(sc,
27317194Seh146360 		    IPW2100_CSR_RX_READ_INDEX);
27323847Seh146360 
27337194Seh146360 		for (; sc->sc_rx_cur != ridx;
27347194Seh146360 		    sc->sc_rx_cur = RING_FORWARD(
27357194Seh146360 		    sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)) {
27367194Seh146360 
27377194Seh146360 			i	= sc->sc_rx_cur;
27387194Seh146360 			status	= &sc->sc_status[i];
27397194Seh146360 			rxbuf	= &sc->sc_rxbufs[i]->rxb_dat[0];
27407194Seh146360 			dr	= &sc->sc_dma_rxbufs[i];
27413847Seh146360 
27427194Seh146360 			/*
27437194Seh146360 			 * sync
27447194Seh146360 			 */
27457194Seh146360 			(void) ddi_dma_sync(sc->sc_dma_status.dr_hnd,
27467194Seh146360 			    i * sizeof (struct ipw2100_status),
27477194Seh146360 			    sizeof (struct ipw2100_status),
27487194Seh146360 			    DDI_DMA_SYNC_FORKERNEL);
27497194Seh146360 			(void) ddi_dma_sync(sc->sc_dma_rxbd.dr_hnd,
27507194Seh146360 			    i * sizeof (struct ipw2100_bd),
27517194Seh146360 			    sizeof (struct ipw2100_bd),
27527194Seh146360 			    DDI_DMA_SYNC_FORKERNEL);
27537194Seh146360 			(void) ddi_dma_sync(dr->dr_hnd, 0,
27547194Seh146360 			    sizeof (struct ipw2100_rxb),
27557194Seh146360 			    DDI_DMA_SYNC_FORKERNEL);
27567194Seh146360 			IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
27577194Seh146360 			    "ipw2100_intr(): status code=0x%04x, len=0x%08x, "
27587194Seh146360 			    "flags=0x%02x, rssi=%02x\n",
27597194Seh146360 			    LE_16(status->code), LE_32(status->len),
27607194Seh146360 			    status->flags, status->rssi));
27617194Seh146360 #if DEBUG
27627194Seh146360 			rxbd	= &sc->sc_rxbd[i];
27637194Seh146360 			IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
27647194Seh146360 			    "ipw2100_intr(): rxbd,phyaddr=0x%08x, len=0x%08x, "
27657194Seh146360 			    "flags=0x%02x,nfrag=%02x\n",
27667194Seh146360 			    LE_32(rxbd->phyaddr), LE_32(rxbd->len),
27677194Seh146360 			    rxbd->flags, rxbd->nfrag));
27687194Seh146360 #endif
27697194Seh146360 			switch (LE_16(status->code) & 0x0f) {
27707194Seh146360 			/*
27717194Seh146360 			 * command complete response
27727194Seh146360 			 */
27737194Seh146360 			case IPW2100_STATUS_CODE_COMMAND:
27747194Seh146360 				mutex_enter(&sc->sc_ilock);
27757194Seh146360 				sc->sc_done = 1;
27767194Seh146360 				cv_signal(&sc->sc_cmd_cond);
27777194Seh146360 				mutex_exit(&sc->sc_ilock);
27787194Seh146360 				break;
27797194Seh146360 			/*
27807194Seh146360 			 * change state
27817194Seh146360 			 */
27827194Seh146360 			case IPW2100_STATUS_CODE_NEWSTATE:
27837194Seh146360 				state = LE_32(* ((uint32_t *)(uintptr_t)rxbuf));
27843847Seh146360 				IPW2100_DBG(IPW2100_DBG_INT,
27853847Seh146360 				    (sc->sc_dip, CE_CONT,
27867194Seh146360 				    "ipw2100_intr(): newstate,state=0x%x\n",
27877194Seh146360 				    state));
27887194Seh146360 
27897194Seh146360 				switch (state) {
27907194Seh146360 				case IPW2100_STATE_ASSOCIATED:
27917194Seh146360 					ieee80211_new_state(ic,
27927194Seh146360 					    IEEE80211_S_RUN, -1);
27937194Seh146360 					break;
27947194Seh146360 				case IPW2100_STATE_ASSOCIATION_LOST:
27957194Seh146360 					case IPW2100_STATE_DISABLED:
27967194Seh146360 					ieee80211_new_state(ic,
27977194Seh146360 					    IEEE80211_S_INIT, -1);
27983847Seh146360 					break;
27993847Seh146360 				/*
28007194Seh146360 				 * When radio is OFF, need a better
28017194Seh146360 				 * scan approach to ensure scan
28027194Seh146360 				 * result correct.
28033847Seh146360 				 */
28047194Seh146360 				case IPW2100_STATE_RADIO_DISABLED:
28057194Seh146360 					IPW2100_REPORT((sc->sc_dip, CE_WARN,
28067194Seh146360 					    "ipw2100_intr(): RADIO is OFF\n"));
28077194Seh146360 					ipw2100_stop(sc);
28087194Seh146360 					break;
28097194Seh146360 				case IPW2100_STATE_SCAN_COMPLETE:
28107194Seh146360 					ieee80211_cancel_scan(ic);
28117194Seh146360 					break;
28127194Seh146360 				case IPW2100_STATE_SCANNING:
28137194Seh146360 					if (ic->ic_state != IEEE80211_S_RUN)
28143847Seh146360 						ieee80211_new_state(ic,
28157194Seh146360 						    IEEE80211_S_SCAN, -1);
28167194Seh146360 					ic->ic_flags |= IEEE80211_F_SCAN;
28173847Seh146360 
28183847Seh146360 					break;
28193847Seh146360 				default:
28203847Seh146360 					break;
28213847Seh146360 				}
28227194Seh146360 				break;
28237194Seh146360 			case IPW2100_STATUS_CODE_DATA_802_11:
28247194Seh146360 			case IPW2100_STATUS_CODE_DATA_802_3:
28257194Seh146360 				ipw2100_rcvpkt(sc, status, rxbuf);
28267194Seh146360 				break;
28277194Seh146360 			case IPW2100_STATUS_CODE_NOTIFICATION:
28287194Seh146360 				break;
28297194Seh146360 			default:
28307194Seh146360 				IPW2100_WARN((sc->sc_dip, CE_WARN,
28317194Seh146360 				    "ipw2100_intr(): "
28327194Seh146360 				    "unknown status code 0x%04x\n",
28337194Seh146360 				    LE_16(status->code)));
28347194Seh146360 				break;
28353847Seh146360 			}
28363847Seh146360 		}
28373847Seh146360 		/*
28387194Seh146360 		 * write sc_rx_cur backward 1 step to RX_WRITE_INDEX
28393847Seh146360 		 */
28407194Seh146360 		ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX,
28417194Seh146360 		    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD));
28423847Seh146360 	}
28433847Seh146360 
28443847Seh146360 	/*
28457194Seh146360 	 * TX intr
28467194Seh146360 	 */
28477194Seh146360 	if (ireg & IPW2100_INTR_TX_TRANSFER) {
28487194Seh146360 		mutex_enter(&sc->sc_tx_lock);
28497194Seh146360 		ridx = ipw2100_csr_get32(sc, IPW2100_CSR_TX_READ_INDEX);
28507194Seh146360 		len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur,
28517194Seh146360 		    sc->sc_tx_free, IPW2100_NUM_TXBD),
28527194Seh146360 		    ridx, IPW2100_NUM_TXBD);
28537194Seh146360 		sc->sc_tx_free += len;
28547194Seh146360 		IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT,
28557194Seh146360 		    "ipw2100_intr(): len=%d\n", len));
28567194Seh146360 		mutex_exit(&sc->sc_tx_lock);
28577194Seh146360 
28587194Seh146360 		mutex_enter(&sc->sc_resched_lock);
28597194Seh146360 		if (len > 1 && (sc->sc_flags & IPW2100_FLAG_TX_SCHED)) {
28607194Seh146360 			sc->sc_flags &= ~IPW2100_FLAG_TX_SCHED;
28617194Seh146360 			mac_tx_update(ic->ic_mach);
28627194Seh146360 		}
28637194Seh146360 		mutex_exit(&sc->sc_resched_lock);
28647194Seh146360 	}
28657194Seh146360 
28667194Seh146360 enable_interrupt:
28677194Seh146360 	/*
28683847Seh146360 	 * enable all interrupts
28693847Seh146360 	 */
28703847Seh146360 	ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL);
28713847Seh146360 
28723847Seh146360 	return (DDI_INTR_CLAIMED);
28733847Seh146360 }
28743847Seh146360 
28753847Seh146360 
28763847Seh146360 /*
28773847Seh146360  * Module Loading Data & Entry Points
28783847Seh146360  */
28793847Seh146360 DDI_DEFINE_STREAM_OPS(ipw2100_devops, nulldev, nulldev, ipw2100_attach,
28807804SKonstantin.Ananyev@Sun.COM     ipw2100_detach, nodev, NULL, D_MP, NULL, ipw2100_quiesce);
28813847Seh146360 
28823847Seh146360 static struct modldrv ipw2100_modldrv = {
28833847Seh146360 	&mod_driverops,
28843847Seh146360 	ipw2100_ident,
28853847Seh146360 	&ipw2100_devops
28863847Seh146360 };
28873847Seh146360 
28883847Seh146360 static struct modlinkage ipw2100_modlinkage = {
28893847Seh146360 	MODREV_1,
28903847Seh146360 	&ipw2100_modldrv,
28913847Seh146360 	NULL
28923847Seh146360 };
28933847Seh146360 
28943847Seh146360 int
_init(void)28953847Seh146360 _init(void)
28963847Seh146360 {
28973847Seh146360 	int	status;
28983847Seh146360 
28993847Seh146360 	status = ddi_soft_state_init(&ipw2100_ssp,
29003847Seh146360 	    sizeof (struct ipw2100_softc), 1);
29013847Seh146360 	if (status != DDI_SUCCESS)
29023847Seh146360 		return (status);
29033847Seh146360 
29043847Seh146360 	mac_init_ops(&ipw2100_devops, IPW2100_DRV_NAME);
29053847Seh146360 	status = mod_install(&ipw2100_modlinkage);
29063847Seh146360 	if (status != DDI_SUCCESS) {
29073847Seh146360 		mac_fini_ops(&ipw2100_devops);
29083847Seh146360 		ddi_soft_state_fini(&ipw2100_ssp);
29093847Seh146360 	}
29103847Seh146360 
29113847Seh146360 	return (status);
29123847Seh146360 }
29133847Seh146360 
29143847Seh146360 int
_fini(void)29153847Seh146360 _fini(void)
29163847Seh146360 {
29173847Seh146360 	int status;
29183847Seh146360 
29193847Seh146360 	status = mod_remove(&ipw2100_modlinkage);
29203847Seh146360 	if (status == DDI_SUCCESS) {
29213847Seh146360 		mac_fini_ops(&ipw2100_devops);
29223847Seh146360 		ddi_soft_state_fini(&ipw2100_ssp);
29233847Seh146360 	}
29243847Seh146360 
29253847Seh146360 	return (status);
29263847Seh146360 }
29273847Seh146360 
29283847Seh146360 int
_info(struct modinfo * mip)29293847Seh146360 _info(struct modinfo *mip)
29303847Seh146360 {
29313847Seh146360 	return (mod_info(&ipw2100_modlinkage, mip));
29323847Seh146360 }
2933