xref: /onnv-gate/usr/src/uts/common/io/iwi/ipw2200.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, 2005
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>
523847Seh146360 #include <sys/mac_wifi.h>
533847Seh146360 #include <sys/varargs.h>
543847Seh146360 #include <sys/pci.h>
553847Seh146360 #include <sys/policy.h>
563847Seh146360 #include <sys/random.h>
575485Seh146360 #include <sys/crypto/common.h>
585485Seh146360 #include <sys/crypto/api.h>
593847Seh146360 
603847Seh146360 #include "ipw2200.h"
613847Seh146360 #include "ipw2200_impl.h"
623847Seh146360 #include <inet/wifi_ioctl.h>
633847Seh146360 
643847Seh146360 /*
655485Seh146360  * for net80211 kernel usage
665485Seh146360  */
675485Seh146360 #include <sys/net80211.h>
685485Seh146360 #include <sys/net80211_proto.h>
695485Seh146360 
705485Seh146360 /*
713847Seh146360  * minimal size reserved in tx-ring
723847Seh146360  */
733847Seh146360 #define	IPW2200_TX_RING_MIN	(8)
743847Seh146360 #define	IPW2200_TXBUF_SIZE	(IEEE80211_MAX_LEN)
753847Seh146360 #define	IPW2200_RXBUF_SIZE	(4096)
763847Seh146360 
773847Seh146360 static void  *ipw2200_ssp = NULL;
787656SSherry.Moore@Sun.COM static char ipw2200_ident[] = IPW2200_DRV_DESC;
793847Seh146360 
803847Seh146360 /*
813847Seh146360  * PIO access attributor for registers
823847Seh146360  */
833847Seh146360 static ddi_device_acc_attr_t ipw2200_csr_accattr = {
843847Seh146360 	DDI_DEVICE_ATTR_V0,
853847Seh146360 	DDI_STRUCTURE_LE_ACC,
863847Seh146360 	DDI_STRICTORDER_ACC
873847Seh146360 };
883847Seh146360 
893847Seh146360 /*
903847Seh146360  * DMA access attributor for descriptors
913847Seh146360  */
923847Seh146360 static ddi_device_acc_attr_t ipw2200_dma_accattr = {
933847Seh146360 	DDI_DEVICE_ATTR_V0,
943847Seh146360 	DDI_NEVERSWAP_ACC,
953847Seh146360 	DDI_STRICTORDER_ACC
963847Seh146360 };
973847Seh146360 
983847Seh146360 /*
993847Seh146360  * Describes the chip's DMA engine
1003847Seh146360  */
1013847Seh146360 static ddi_dma_attr_t ipw2200_dma_attr = {
1023847Seh146360 	DMA_ATTR_V0,		/* version */
1033847Seh146360 	0x0000000000000000ULL,  /* addr_lo */
1043847Seh146360 	0x00000000ffffffffULL,  /* addr_hi */
1053847Seh146360 	0x00000000ffffffffULL,  /* counter */
1063847Seh146360 	0x0000000000000004ULL,  /* alignment */
1073847Seh146360 	0xfff,			/* burst */
1083847Seh146360 	1,			/* min xfer */
1093847Seh146360 	0x00000000ffffffffULL,  /* max xfer */
1103847Seh146360 	0x00000000ffffffffULL,  /* seg boud */
1113847Seh146360 	1,			/* s/g list */
1123847Seh146360 	1,			/* granularity */
1133847Seh146360 	0			/* flags */
1143847Seh146360 };
1153847Seh146360 
1163847Seh146360 static uint8_t ipw2200_broadcast_addr[] = {
1173847Seh146360 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1183847Seh146360 };
1193847Seh146360 static const struct ieee80211_rateset ipw2200_rateset_11a = { 8,
1203847Seh146360 	{12, 18, 24, 36, 48, 72, 96, 108}
1213847Seh146360 };
1223847Seh146360 static const struct ieee80211_rateset ipw2200_rateset_11b = { 4,
1233847Seh146360 	{2, 4, 11, 22}
1243847Seh146360 };
1253847Seh146360 static const struct ieee80211_rateset ipw2200_rateset_11g = { 12,
1263847Seh146360 	{2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
1273847Seh146360 };
1283847Seh146360 
1293847Seh146360 /*
1303847Seh146360  * Used by multi function thread
1313847Seh146360  */
1323847Seh146360 extern pri_t minclsyspri;
1333847Seh146360 
1343847Seh146360 /*
1353847Seh146360  * ipw2200 specific hardware operations
1363847Seh146360  */
1373847Seh146360 static void	ipw2200_hwconf_get(struct ipw2200_softc *sc);
1383847Seh146360 static int	ipw2200_chip_reset(struct ipw2200_softc *sc);
1393847Seh146360 static void	ipw2200_master_stop(struct ipw2200_softc *sc);
1403847Seh146360 static void	ipw2200_stop(struct ipw2200_softc *sc);
1413847Seh146360 static int	ipw2200_config(struct ipw2200_softc *sc);
1423847Seh146360 static int	ipw2200_cmd(struct ipw2200_softc *sc,
1433847Seh146360     uint32_t type, void *buf, size_t len, int async);
1443847Seh146360 static void	ipw2200_ring_hwsetup(struct ipw2200_softc *sc);
1453847Seh146360 static int	ipw2200_ring_alloc(struct ipw2200_softc *sc);
1463847Seh146360 static void	ipw2200_ring_free(struct ipw2200_softc *sc);
1473847Seh146360 static void	ipw2200_ring_reset(struct ipw2200_softc *sc);
1483847Seh146360 static int	ipw2200_ring_init(struct ipw2200_softc *sc);
1493847Seh146360 
1503847Seh146360 /*
1513847Seh146360  * GLD specific operations
1523847Seh146360  */
1533847Seh146360 static int	ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val);
1543847Seh146360 static int	ipw2200_m_start(void *arg);
1553847Seh146360 static void	ipw2200_m_stop(void *arg);
1563847Seh146360 static int	ipw2200_m_unicst(void *arg, const uint8_t *macaddr);
1573847Seh146360 static int	ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *m);
1583847Seh146360 static int	ipw2200_m_promisc(void *arg, boolean_t on);
1593847Seh146360 static void	ipw2200_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
1603847Seh146360 static mblk_t  *ipw2200_m_tx(void *arg, mblk_t *mp);
1618367SFei.Feng@Sun.COM static int	ipw2200_m_setprop(void *arg, const char *pr_name,
1628367SFei.Feng@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
1638367SFei.Feng@Sun.COM static int	ipw2200_m_getprop(void *arg, const char *pr_name,
164*11878SVenu.Iyer@Sun.COM     mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
165*11878SVenu.Iyer@Sun.COM static void	ipw2200_m_propinfo(void *arg, const char *pr_name,
166*11878SVenu.Iyer@Sun.COM     mac_prop_id_t wldp_pr_num, mac_prop_info_handle_t mph);
1673847Seh146360 
1683847Seh146360 /*
1693847Seh146360  * Interrupt and Data transferring operations
1703847Seh146360  */
1713847Seh146360 static uint_t	ipw2200_intr(caddr_t arg);
1723847Seh146360 static int	ipw2200_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type);
1733847Seh146360 static void	ipw2200_rcv_frame(struct ipw2200_softc *sc,
1743847Seh146360     struct ipw2200_frame *frame);
1753847Seh146360 static void	ipw2200_rcv_notif(struct ipw2200_softc *sc,
1763847Seh146360     struct ipw2200_notif *notif);
1773847Seh146360 
1783847Seh146360 /*
1793847Seh146360  * WiFi specific operations
1803847Seh146360  */
1813847Seh146360 static int	ipw2200_newstate(struct ieee80211com *ic,
1823847Seh146360     enum ieee80211_state state, int arg);
1833847Seh146360 static void	ipw2200_thread(struct ipw2200_softc *sc);
1843847Seh146360 
1853847Seh146360 /*
1863847Seh146360  * IOCTL Handler
1873847Seh146360  */
1883847Seh146360 static int	ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m);
1893847Seh146360 static int	ipw2200_getset(struct ipw2200_softc *sc,
1903847Seh146360     mblk_t *m, uint32_t cmd, boolean_t *need_net80211);
1913847Seh146360 static int	iwi_wificfg_radio(struct ipw2200_softc *sc,
1923847Seh146360     uint32_t cmd,  wldp_t *outfp);
1933847Seh146360 static int	iwi_wificfg_desrates(wldp_t *outfp);
1943847Seh146360 
1953847Seh146360 /*
1965485Seh146360  * net80211 functions
1975485Seh146360  */
1985485Seh146360 extern uint8_t	ieee80211_crypto_getciphertype(ieee80211com_t *ic);
1995485Seh146360 extern void	ieee80211_notify_node_join(ieee80211com_t *ic,
2005485Seh146360     ieee80211_node_t *in);
2015485Seh146360 extern void	ieee80211_notify_node_leave(ieee80211com_t *ic,
2025485Seh146360     ieee80211_node_t *in);
2035485Seh146360 
2045485Seh146360 /*
2053847Seh146360  * Mac Call Back entries
2063847Seh146360  */
2073847Seh146360 mac_callbacks_t	ipw2200_m_callbacks = {
208*11878SVenu.Iyer@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
2093847Seh146360 	ipw2200_m_stat,
2103847Seh146360 	ipw2200_m_start,
2113847Seh146360 	ipw2200_m_stop,
2123847Seh146360 	ipw2200_m_promisc,
2133847Seh146360 	ipw2200_m_multicst,
2143847Seh146360 	ipw2200_m_unicst,
2153847Seh146360 	ipw2200_m_tx,
216*11878SVenu.Iyer@Sun.COM 	NULL,
2178367SFei.Feng@Sun.COM 	ipw2200_m_ioctl,
2188367SFei.Feng@Sun.COM 	NULL,
2198367SFei.Feng@Sun.COM 	NULL,
2208367SFei.Feng@Sun.COM 	NULL,
2218367SFei.Feng@Sun.COM 	ipw2200_m_setprop,
222*11878SVenu.Iyer@Sun.COM 	ipw2200_m_getprop,
223*11878SVenu.Iyer@Sun.COM 	ipw2200_m_propinfo
2243847Seh146360 };
2253847Seh146360 
2263847Seh146360 /*
2273847Seh146360  * DEBUG Facility
2283847Seh146360  */
2293847Seh146360 #define		MAX_MSG		(128)
2303847Seh146360 uint32_t	ipw2200_debug = 0;
2313847Seh146360 /*
2323847Seh146360  * supported debug marks are:
2333847Seh146360  *	| IPW2200_DBG_CSR
2343847Seh146360  *	| IPW2200_DBG_TABLE
2353847Seh146360  *	| IPW2200_DBG_HWCAP
2363847Seh146360  *	| IPW2200_DBG_TX
2373847Seh146360  *	| IPW2200_DBG_INIT
2383847Seh146360  *	| IPW2200_DBG_FW
2393847Seh146360  *	| IPW2200_DBG_NOTIF
2403847Seh146360  *	| IPW2200_DBG_SCAN
2413847Seh146360  *	| IPW2200_DBG_IOCTL
2423847Seh146360  *	| IPW2200_DBG_RING
2433847Seh146360  *	| IPW2200_DBG_INT
2443847Seh146360  *	| IPW2200_DBG_RX
2453847Seh146360  *	| IPW2200_DBG_DMA
2463847Seh146360  *	| IPW2200_DBG_GLD
2473847Seh146360  *	| IPW2200_DBG_WIFI
2483847Seh146360  *	| IPW2200_DBG_SOFTINT
2497194Seh146360  *	| IPW2200_DBG_SUSPEND
2508367SFei.Feng@Sun.COM  *	| IPW2200_DBG_BRUSSELS
2513847Seh146360  */
2523847Seh146360 
2533847Seh146360 /*
2543847Seh146360  * Global tunning parameter to work around unknown hardware issues
2553847Seh146360  */
2563847Seh146360 static uint32_t delay_config_stable	= 100000;	/* 100ms */
2573847Seh146360 static uint32_t delay_fatal_recover	= 100000 * 20;	/* 2s */
2583847Seh146360 static uint32_t delay_aux_thread	= 100000;	/* 100ms */
2593847Seh146360 
2603847Seh146360 #define	IEEE80211_IS_CHAN_2GHZ(_c) \
2613847Seh146360 	(((_c)->ich_flags & IEEE80211_CHAN_2GHZ) != 0)
2623847Seh146360 #define	IEEE80211_IS_CHAN_5GHZ(_c) \
2633847Seh146360 	(((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0)
2643847Seh146360 #define	isset(a, i)	((a)[(i)/NBBY] & (1 << ((i)%NBBY)))
2653847Seh146360 
2663847Seh146360 void
ipw2200_dbg(dev_info_t * dip,int level,const char * fmt,...)2673847Seh146360 ipw2200_dbg(dev_info_t *dip, int level, const char *fmt, ...)
2683847Seh146360 {
2693847Seh146360 	va_list	ap;
2703847Seh146360 	char    buf[MAX_MSG];
2713847Seh146360 	int	instance;
2723847Seh146360 
2733847Seh146360 	va_start(ap, fmt);
2743847Seh146360 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
2753847Seh146360 	va_end(ap);
2763847Seh146360 
2773847Seh146360 	if (dip) {
2783847Seh146360 		instance = ddi_get_instance(dip);
2793847Seh146360 		cmn_err(level, "%s%d: %s", IPW2200_DRV_NAME, instance, buf);
2803847Seh146360 	} else
2813847Seh146360 		cmn_err(level, "%s: %s", IPW2200_DRV_NAME, buf);
2823847Seh146360 
2833847Seh146360 }
2843847Seh146360 
2853847Seh146360 /*
2867194Seh146360  * Set up pci
2877194Seh146360  */
2887194Seh146360 int
ipw2200_setup_pci(dev_info_t * dip,struct ipw2200_softc * sc)2897194Seh146360 ipw2200_setup_pci(dev_info_t *dip, struct ipw2200_softc *sc)
2907194Seh146360 {
2917194Seh146360 	ddi_acc_handle_t	cfgh;
2927194Seh146360 	caddr_t			regs;
2937194Seh146360 	int			err;
2947194Seh146360 
2957194Seh146360 	/*
2967194Seh146360 	 * Map config spaces register to read the vendor id, device id, sub
2977194Seh146360 	 * vendor id, and sub device id.
2987194Seh146360 	 */
2997194Seh146360 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CFG_RNUM, &regs,
3007194Seh146360 	    0, 0, &ipw2200_csr_accattr, &cfgh);
3017194Seh146360 	if (err != DDI_SUCCESS) {
3027194Seh146360 		IPW2200_WARN((dip, CE_WARN,
3037194Seh146360 		    "ipw2200_attach(): unable to map spaces regs\n"));
3047194Seh146360 		return (DDI_FAILURE);
3057194Seh146360 	}
3067194Seh146360 
3077194Seh146360 	ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0);
3087194Seh146360 	sc->sc_vendor = ddi_get16(cfgh,
3097194Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_VENID));
3107194Seh146360 	sc->sc_device = ddi_get16(cfgh,
3117194Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_DEVID));
3127194Seh146360 	sc->sc_subven = ddi_get16(cfgh,
3137194Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBVENID));
3147194Seh146360 	sc->sc_subdev = ddi_get16(cfgh,
3157194Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBSYSID));
3167194Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
3177194Seh146360 	    "ipw2200_setup_pci(): vendor = 0x%04x, devic = 0x%04x,"
3187194Seh146360 	    "subversion = 0x%04x, subdev = 0x%04x",
3197194Seh146360 	    sc->sc_vendor, sc->sc_device, sc->sc_subven, sc->sc_subdev));
3207194Seh146360 
3217194Seh146360 	ddi_regs_map_free(&cfgh);
3227194Seh146360 
3237194Seh146360 	return (DDI_SUCCESS);
3247194Seh146360 
3257194Seh146360 }
3267194Seh146360 
3277194Seh146360 /*
3283847Seh146360  * Device operations
3293847Seh146360  */
3303847Seh146360 int
ipw2200_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3313847Seh146360 ipw2200_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3323847Seh146360 {
3333847Seh146360 	struct ipw2200_softc	*sc;
3343847Seh146360 	struct ieee80211com	*ic;
3353847Seh146360 	int			instance, err, i;
3363847Seh146360 	char			strbuf[32];
3373847Seh146360 	wifi_data_t		wd = { 0 };
3383847Seh146360 	mac_register_t		*macp;
3397194Seh146360 
3407194Seh146360 	switch (cmd) {
3417194Seh146360 	case DDI_ATTACH:
3427194Seh146360 		break;
3437194Seh146360 	case DDI_RESUME:
3447194Seh146360 		sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
3457194Seh146360 		ASSERT(sc != NULL);
3467194Seh146360 
3477194Seh146360 		/*
3487194Seh146360 		 * set up pci
3497194Seh146360 		 */
3507194Seh146360 		err = ipw2200_setup_pci(dip, sc);
3517194Seh146360 		if (err != DDI_SUCCESS) {
3527194Seh146360 			IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
3537194Seh146360 			    "ipw2200_attach(): resume failure\n"));
3547194Seh146360 			return (DDI_FAILURE);
3557194Seh146360 		}
3567194Seh146360 
3577194Seh146360 		/*
3587194Seh146360 		 * resume hardware.
3597194Seh146360 		 * If it was on runnning status, reset to INIT state
3607194Seh146360 		 */
3617194Seh146360 		sc->sc_flags &= ~IPW2200_FLAG_SUSPEND;
3627194Seh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING)
3637194Seh146360 			(void) ipw2200_init(sc);
3647194Seh146360 
3657194Seh146360 		IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
3667194Seh146360 		    "ipw2200_attach(): resume successful\n"));
3677194Seh146360 		return (DDI_SUCCESS);
3687194Seh146360 	default:
3697194Seh146360 		return (DDI_FAILURE);
3703847Seh146360 	}
3713847Seh146360 
3723847Seh146360 	instance = ddi_get_instance(dip);
3733847Seh146360 	err = ddi_soft_state_zalloc(ipw2200_ssp, instance);
3743847Seh146360 	if (err != DDI_SUCCESS) {
3753847Seh146360 		IPW2200_WARN((dip, CE_WARN,
3763847Seh146360 		    "ipw2200_attach(): unable to allocate soft state\n"));
3773847Seh146360 		goto fail1;
3783847Seh146360 	}
3793847Seh146360 	sc = ddi_get_soft_state(ipw2200_ssp, instance);
3803847Seh146360 	sc->sc_dip = dip;
3813847Seh146360 
3827194Seh146360 	/* set up pci, put reg+0x41 0 */
3837194Seh146360 	err = ipw2200_setup_pci(dip, sc);
3843847Seh146360 	if (err != DDI_SUCCESS) {
3853847Seh146360 		IPW2200_WARN((dip, CE_WARN,
3867194Seh146360 		    "ipw2200_attach(): unable to setup pci\n"));
3873847Seh146360 		goto fail2;
3883847Seh146360 	}
3893847Seh146360 
3903847Seh146360 	/*
3913847Seh146360 	 * Map operating registers
3923847Seh146360 	 */
3933847Seh146360 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CSR_RNUM, &sc->sc_regs,
3943847Seh146360 	    0, 0, &ipw2200_csr_accattr, &sc->sc_ioh);
3953847Seh146360 	if (err != DDI_SUCCESS) {
3963847Seh146360 		IPW2200_WARN((dip, CE_WARN,
3973847Seh146360 		    "ipw2200_attach(): ddi_regs_map_setup() failed\n"));
3983847Seh146360 		goto fail2;
3993847Seh146360 	}
4003847Seh146360 
4013847Seh146360 	/*
4023847Seh146360 	 * Reset the chip
4033847Seh146360 	 */
4043847Seh146360 	err = ipw2200_chip_reset(sc);
4053847Seh146360 	if (err != DDI_SUCCESS) {
4063847Seh146360 		IPW2200_WARN((dip, CE_WARN,
4073847Seh146360 		    "ipw2200_attach(): ipw2200_chip_reset() failed\n"));
4083847Seh146360 		goto fail3;
4093847Seh146360 	}
4103847Seh146360 
4113847Seh146360 	/*
4123847Seh146360 	 * Get the hardware configuration, including the MAC address
4133847Seh146360 	 * Then, init all the rings needed.
4143847Seh146360 	 */
4153847Seh146360 	ipw2200_hwconf_get(sc);
4163847Seh146360 	err = ipw2200_ring_init(sc);
4173847Seh146360 	if (err != DDI_SUCCESS) {
4183847Seh146360 		IPW2200_WARN((dip, CE_WARN,
4193847Seh146360 		    "ipw2200_attach(): ipw2200_ring_init() failed\n"));
4203847Seh146360 		goto fail3;
4213847Seh146360 	}
4223847Seh146360 
4233847Seh146360 	/*
4243847Seh146360 	 * Initialize mutexs and condvars
4253847Seh146360 	 */
4263847Seh146360 	err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk);
4273847Seh146360 	if (err != DDI_SUCCESS) {
4283847Seh146360 		IPW2200_WARN((dip, CE_WARN,
4293847Seh146360 		    "ipw2200_attach(): ddi_get_iblock_cookie() failed\n"));
4303847Seh146360 		goto fail4;
4313847Seh146360 	}
4323847Seh146360 
4333847Seh146360 	/*
4343847Seh146360 	 * interrupt lock
4353847Seh146360 	 */
4363847Seh146360 	mutex_init(&sc->sc_ilock, "intr-lock", MUTEX_DRIVER,
4373847Seh146360 	    (void *) sc->sc_iblk);
4383847Seh146360 	cv_init(&sc->sc_fw_cond, "firmware-ok", CV_DRIVER, NULL);
4393847Seh146360 	cv_init(&sc->sc_cmd_status_cond, "cmd-status-ring", CV_DRIVER, NULL);
4403847Seh146360 
4413847Seh146360 	/*
4423847Seh146360 	 * command ring lock
4433847Seh146360 	 */
4443847Seh146360 	mutex_init(&sc->sc_cmd_lock, "cmd-ring", MUTEX_DRIVER,
4453847Seh146360 	    (void *) sc->sc_iblk);
4463847Seh146360 	cv_init(&sc->sc_cmd_cond, "cmd-ring", CV_DRIVER, NULL);
4473847Seh146360 
4483847Seh146360 	/*
4493847Seh146360 	 * tx ring lock
4503847Seh146360 	 */
4513847Seh146360 	mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER,
4523847Seh146360 	    (void *) sc->sc_iblk);
4533847Seh146360 
4543847Seh146360 	/*
4555485Seh146360 	 * rescheduled lock
4565485Seh146360 	 */
4575485Seh146360 	mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER,
4585485Seh146360 	    (void *) sc->sc_iblk);
4595485Seh146360 
4605485Seh146360 	/*
4613847Seh146360 	 * multi-function lock, may acquire this during interrupt
4623847Seh146360 	 */
4633847Seh146360 	mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER,
4643847Seh146360 	    (void *) sc->sc_iblk);
4653847Seh146360 	cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL);
4663847Seh146360 	sc->sc_mf_thread = NULL;
4673847Seh146360 	sc->sc_mfthread_switch = 0;
4683847Seh146360 
4693847Seh146360 	/*
4705485Seh146360 	 * Initialize the WiFi part
4713847Seh146360 	 */
4723847Seh146360 	ic = &sc->sc_ic;
4733847Seh146360 	ic->ic_phytype  = IEEE80211_T_OFDM;
4743847Seh146360 	ic->ic_opmode   = IEEE80211_M_STA;
4753847Seh146360 	ic->ic_state    = IEEE80211_S_INIT;
4763847Seh146360 	ic->ic_maxrssi  = 100; /* experimental number */
4775485Seh146360 	ic->ic_caps =
4785485Seh146360 	    IEEE80211_C_SHPREAMBLE |
4795485Seh146360 	    IEEE80211_C_TXPMGT |
4805485Seh146360 	    IEEE80211_C_PMGT |
4815485Seh146360 	    IEEE80211_C_WPA;
4823847Seh146360 
4833847Seh146360 	/*
4843847Seh146360 	 * set mac addr
4853847Seh146360 	 */
4863847Seh146360 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr);
4873847Seh146360 
4883847Seh146360 	/*
4893847Seh146360 	 * set supported .11a rates and channel - (2915ABG only)
4903847Seh146360 	 */
4917194Seh146360 	if (sc->sc_device >= 0x4223) {
4923847Seh146360 		/* .11a rates */
4933847Seh146360 		ic->ic_sup_rates[IEEE80211_MODE_11A] = ipw2200_rateset_11a;
4943847Seh146360 		/* .11a channels */
4953847Seh146360 		for (i = 36; i <= 64; i += 4) {
4963847Seh146360 			ic->ic_sup_channels[i].ich_freq =
4973847Seh146360 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
4983847Seh146360 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
4993847Seh146360 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
5003847Seh146360 		}
5013847Seh146360 		for (i = 149; i <= 165; i += 4) {
5023847Seh146360 			ic->ic_sup_channels[i].ich_freq =
5033847Seh146360 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
5043847Seh146360 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
5053847Seh146360 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
5063847Seh146360 		}
5073847Seh146360 	}
5083847Seh146360 
5093847Seh146360 	/*
5103847Seh146360 	 * set supported .11b and .11g rates
5113847Seh146360 	 */
5123847Seh146360 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2200_rateset_11b;
5133847Seh146360 	ic->ic_sup_rates[IEEE80211_MODE_11G] = ipw2200_rateset_11g;
5143847Seh146360 
5153847Seh146360 	/*
5163847Seh146360 	 * set supported .11b and .11g channels(1 through 14)
5173847Seh146360 	 */
5183847Seh146360 	for (i = 1; i < 14; i++) {
5193847Seh146360 		ic->ic_sup_channels[i].ich_freq  =
5203847Seh146360 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
5213847Seh146360 		ic->ic_sup_channels[i].ich_flags =
5223847Seh146360 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
5233847Seh146360 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
5243847Seh146360 	}
5253847Seh146360 
5263847Seh146360 	/*
5273847Seh146360 	 * IBSS channal undefined for now
5283847Seh146360 	 */
5293847Seh146360 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
5303847Seh146360 	ic->ic_xmit = ipw2200_send;
5313847Seh146360 
5323847Seh146360 	/*
5333847Seh146360 	 * init generic layer, then override state transition machine
5343847Seh146360 	 */
5353847Seh146360 	ieee80211_attach(ic);
5363847Seh146360 
5373847Seh146360 	/*
5385485Seh146360 	 * different instance has different WPA door
5395485Seh146360 	 */
5405485Seh146360 	ieee80211_register_door(ic, ddi_driver_name(dip), instance);
5415485Seh146360 
5425485Seh146360 	/*
5433847Seh146360 	 * Override 80211 default routines
5443847Seh146360 	 */
5453847Seh146360 	ieee80211_media_init(ic); /* initial the node table and bss */
5463847Seh146360 	sc->sc_newstate = ic->ic_newstate;
5473847Seh146360 	ic->ic_newstate = ipw2200_newstate;
5483847Seh146360 	ic->ic_def_txkey = 0;
5493847Seh146360 	sc->sc_authmode = IEEE80211_AUTH_OPEN;
5503847Seh146360 
5513847Seh146360 	/*
5523847Seh146360 	 * Add the interrupt handler
5533847Seh146360 	 */
5543847Seh146360 	err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL,
5553847Seh146360 	    ipw2200_intr, (caddr_t)sc);
5563847Seh146360 	if (err != DDI_SUCCESS) {
5573847Seh146360 		IPW2200_WARN((dip, CE_WARN,
5583847Seh146360 		    "ipw2200_attach(): ddi_add_intr() failed\n"));
5593847Seh146360 		goto fail5;
5603847Seh146360 	}
5613847Seh146360 
5623847Seh146360 	/*
5633847Seh146360 	 * Initialize pointer to device specific functions
5643847Seh146360 	 */
5653847Seh146360 	wd.wd_secalloc = WIFI_SEC_NONE;
5663847Seh146360 	wd.wd_opmode = ic->ic_opmode;
5675485Seh146360 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
5683847Seh146360 
5693847Seh146360 	macp = mac_alloc(MAC_VERSION);
5703847Seh146360 	if (err != 0) {
5713847Seh146360 		IPW2200_WARN((dip, CE_WARN,
5723847Seh146360 		    "ipw2200_attach(): mac_alloc() failed\n"));
5733847Seh146360 		goto fail6;
5743847Seh146360 	}
5753847Seh146360 
5763847Seh146360 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
5773847Seh146360 	macp->m_driver		= sc;
5783847Seh146360 	macp->m_dip		= dip;
5793847Seh146360 	macp->m_src_addr	= ic->ic_macaddr;
5803847Seh146360 	macp->m_callbacks	= &ipw2200_m_callbacks;
5813847Seh146360 	macp->m_min_sdu		= 0;
5823847Seh146360 	macp->m_max_sdu		= IEEE80211_MTU;
5833847Seh146360 	macp->m_pdata		= &wd;
5843847Seh146360 	macp->m_pdata_size	= sizeof (wd);
5853847Seh146360 
5863847Seh146360 	/*
5873847Seh146360 	 * Register the macp to mac
5883847Seh146360 	 */
5893847Seh146360 	err = mac_register(macp, &ic->ic_mach);
5903847Seh146360 	mac_free(macp);
5913847Seh146360 	if (err != DDI_SUCCESS) {
5923847Seh146360 		IPW2200_WARN((dip, CE_WARN,
5933847Seh146360 		    "ipw2200_attach(): mac_register() failed\n"));
5943847Seh146360 		goto fail6;
5953847Seh146360 	}
5963847Seh146360 
5973847Seh146360 	/*
5983847Seh146360 	 * Create minor node of type DDI_NT_NET_WIFI
5993847Seh146360 	 */
6003847Seh146360 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
6013847Seh146360 	    IPW2200_DRV_NAME, instance);
6023847Seh146360 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
6033847Seh146360 	    instance + 1, DDI_NT_NET_WIFI, 0);
6043847Seh146360 	if (err != DDI_SUCCESS)
6053847Seh146360 		IPW2200_WARN((dip, CE_WARN,
6063847Seh146360 		    "ipw2200_attach(): ddi_create_minor_node() failed\n"));
6073847Seh146360 
6083847Seh146360 	/*
6093847Seh146360 	 * Cache firmware will always be true
6103847Seh146360 	 */
6113847Seh146360 	(void) ipw2200_cache_firmware(sc);
6123847Seh146360 
6133847Seh146360 	/*
6143847Seh146360 	 * Notify link is down now
6153847Seh146360 	 */
6163847Seh146360 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
6173847Seh146360 
6183847Seh146360 	/*
6193847Seh146360 	 * Create the mf thread to handle the link status,
6203847Seh146360 	 * recovery fatal error, etc.
6213847Seh146360 	 */
6223847Seh146360 	sc->sc_mfthread_switch = 1;
6233847Seh146360 	if (sc->sc_mf_thread == NULL)
6243847Seh146360 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
6253847Seh146360 		    ipw2200_thread, sc, 0, &p0, TS_RUN, minclsyspri);
6263847Seh146360 
6273847Seh146360 	return (DDI_SUCCESS);
6283847Seh146360 
6293847Seh146360 fail6:
6303847Seh146360 	ddi_remove_intr(dip, 0, sc->sc_iblk);
6313847Seh146360 fail5:
6323847Seh146360 	ieee80211_detach(ic);
6333847Seh146360 
6343847Seh146360 	mutex_destroy(&sc->sc_ilock);
6353847Seh146360 	mutex_destroy(&sc->sc_cmd_lock);
6363847Seh146360 	mutex_destroy(&sc->sc_tx_lock);
6373847Seh146360 	mutex_destroy(&sc->sc_mflock);
6385485Seh146360 	mutex_destroy(&sc->sc_resched_lock);
6393847Seh146360 	cv_destroy(&sc->sc_fw_cond);
6403847Seh146360 	cv_destroy(&sc->sc_cmd_status_cond);
6413847Seh146360 	cv_destroy(&sc->sc_cmd_cond);
6423847Seh146360 	cv_destroy(&sc->sc_mfthread_cv);
6433847Seh146360 fail4:
6443847Seh146360 	ipw2200_ring_free(sc);
6453847Seh146360 fail3:
6463847Seh146360 	ddi_regs_map_free(&sc->sc_ioh);
6473847Seh146360 fail2:
6483847Seh146360 	ddi_soft_state_free(ipw2200_ssp, instance);
6493847Seh146360 fail1:
6503847Seh146360 	return (err);
6513847Seh146360 }
6523847Seh146360 
6533847Seh146360 
6543847Seh146360 int
ipw2200_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6553847Seh146360 ipw2200_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6563847Seh146360 {
6577194Seh146360 	struct ipw2200_softc	*sc;
6583847Seh146360 	int			err;
6597194Seh146360 
6607194Seh146360 	sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
6613847Seh146360 	ASSERT(sc != NULL);
6623847Seh146360 
6637194Seh146360 	switch (cmd) {
6647194Seh146360 	case DDI_DETACH:
6657194Seh146360 		break;
6667194Seh146360 	case DDI_SUSPEND:
6677194Seh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
6687194Seh146360 			ipw2200_stop(sc);
6697194Seh146360 		}
6707194Seh146360 		sc->sc_flags |= IPW2200_FLAG_SUSPEND;
6717194Seh146360 
6727194Seh146360 		IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
6737194Seh146360 		    "ipw2200_detach(): suspend\n"));
6747194Seh146360 		return (DDI_SUCCESS);
6757194Seh146360 	default:
6763847Seh146360 		return (DDI_FAILURE);
6777194Seh146360 	}
6783847Seh146360 
6797507SXinghua.Wen@Sun.COM 	err = mac_disable(sc->sc_ic.ic_mach);
6807507SXinghua.Wen@Sun.COM 	if (err != DDI_SUCCESS)
6817507SXinghua.Wen@Sun.COM 		return (err);
6827507SXinghua.Wen@Sun.COM 
6833847Seh146360 	ipw2200_stop(sc);
6843847Seh146360 
6853847Seh146360 	/*
6863847Seh146360 	 * Destroy the mf_thread
6873847Seh146360 	 */
6883847Seh146360 	mutex_enter(&sc->sc_mflock);
6893847Seh146360 	sc->sc_mfthread_switch = 0;
6903847Seh146360 	while (sc->sc_mf_thread != NULL) {
6913847Seh146360 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
6923847Seh146360 			break;
6933847Seh146360 	}
6943847Seh146360 	mutex_exit(&sc->sc_mflock);
6953847Seh146360 
6963847Seh146360 	/*
6973847Seh146360 	 * Unregister from the MAC layer subsystem
6983847Seh146360 	 */
6997507SXinghua.Wen@Sun.COM 	(void) mac_unregister(sc->sc_ic.ic_mach);
7003847Seh146360 
7013847Seh146360 	ddi_remove_intr(dip, IPW2200_PCI_INTR_NUM, sc->sc_iblk);
7023847Seh146360 
7033847Seh146360 	mutex_destroy(&sc->sc_ilock);
7043847Seh146360 	mutex_destroy(&sc->sc_cmd_lock);
7053847Seh146360 	mutex_destroy(&sc->sc_tx_lock);
7063847Seh146360 	mutex_destroy(&sc->sc_mflock);
7075485Seh146360 	mutex_destroy(&sc->sc_resched_lock);
7083847Seh146360 	cv_destroy(&sc->sc_fw_cond);
7093847Seh146360 	cv_destroy(&sc->sc_cmd_status_cond);
7103847Seh146360 	cv_destroy(&sc->sc_cmd_cond);
7113847Seh146360 	cv_destroy(&sc->sc_mfthread_cv);
7123847Seh146360 
7133847Seh146360 	/*
7143847Seh146360 	 * Detach ieee80211
7153847Seh146360 	 */
7163847Seh146360 	ieee80211_detach(&sc->sc_ic);
7173847Seh146360 
7183847Seh146360 	(void) ipw2200_free_firmware(sc);
7193847Seh146360 	ipw2200_ring_free(sc);
7203847Seh146360 
7213847Seh146360 	ddi_regs_map_free(&sc->sc_ioh);
7223847Seh146360 	ddi_remove_minor_node(dip, NULL);
7233847Seh146360 	ddi_soft_state_free(ipw2200_ssp, ddi_get_instance(dip));
7243847Seh146360 
7253847Seh146360 	return (DDI_SUCCESS);
7263847Seh146360 }
7273847Seh146360 
7287809SFei.Feng@Sun.COM /*
7297809SFei.Feng@Sun.COM  * quiesce(9E) entry point.
7307809SFei.Feng@Sun.COM  * This function is called when the system is single-threaded at high
7317809SFei.Feng@Sun.COM  * PIL with preemption disabled. Therefore, this function must not be
7327809SFei.Feng@Sun.COM  * blocked.
7337809SFei.Feng@Sun.COM  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
7347809SFei.Feng@Sun.COM  * DDI_FAILURE indicates an error condition and should almost never happen.
7357809SFei.Feng@Sun.COM  */
7367809SFei.Feng@Sun.COM static int
ipw2200_quiesce(dev_info_t * dip)7377809SFei.Feng@Sun.COM ipw2200_quiesce(dev_info_t *dip)
7384812Seh146360 {
7394812Seh146360 	struct ipw2200_softc	*sc =
7404812Seh146360 	    ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
7417809SFei.Feng@Sun.COM 	if (sc == NULL)
7427809SFei.Feng@Sun.COM 		return (DDI_FAILURE);
7437809SFei.Feng@Sun.COM 
7448165SFei.Feng@Sun.COM 	/* by pass any messages, if it's quiesce */
7458165SFei.Feng@Sun.COM 	ipw2200_debug = 0;
7468165SFei.Feng@Sun.COM 
7477809SFei.Feng@Sun.COM 	/*
7487809SFei.Feng@Sun.COM 	 * No more blocking is allowed while we are in the
7497809SFei.Feng@Sun.COM 	 * quiesce(9E) entry point.
7507809SFei.Feng@Sun.COM 	 */
7517809SFei.Feng@Sun.COM 	sc->sc_flags |= IPW2200_FLAG_QUIESCED;
7527809SFei.Feng@Sun.COM 
7537809SFei.Feng@Sun.COM 	/*
7547809SFei.Feng@Sun.COM 	 * Disable and mask all interrupts.
7557809SFei.Feng@Sun.COM 	 */
7568165SFei.Feng@Sun.COM 	ipw2200_master_stop(sc);
7578165SFei.Feng@Sun.COM 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET);
7584812Seh146360 	return (DDI_SUCCESS);
7594812Seh146360 }
7604812Seh146360 
7613847Seh146360 static void
ipw2200_stop(struct ipw2200_softc * sc)7623847Seh146360 ipw2200_stop(struct ipw2200_softc *sc)
7633847Seh146360 {
7643847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
7653847Seh146360 
7663847Seh146360 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
7673847Seh146360 	    "ipw2200_stop(): enter\n"));
7683847Seh146360 
7693847Seh146360 	ipw2200_master_stop(sc);
7703847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET);
7713847Seh146360 
7723847Seh146360 	/*
7733847Seh146360 	 * Reset ring
7743847Seh146360 	 */
7753847Seh146360 	ipw2200_ring_reset(sc);
7763847Seh146360 
7778165SFei.Feng@Sun.COM 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
7783847Seh146360 	sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
7795485Seh146360 	sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED;
7803847Seh146360 
7818165SFei.Feng@Sun.COM 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
7828165SFei.Feng@Sun.COM 	    "ipw2200_stop(): exit\n"));
7833847Seh146360 }
7843847Seh146360 
7853847Seh146360 static int
ipw2200_config(struct ipw2200_softc * sc)7863847Seh146360 ipw2200_config(struct ipw2200_softc *sc)
7873847Seh146360 {
7883847Seh146360 	struct ieee80211com		*ic = &sc->sc_ic;
7893847Seh146360 	struct ipw2200_configuration	cfg;
7903847Seh146360 	uint32_t			data;
7913847Seh146360 	struct ipw2200_txpower		pwr;
7923847Seh146360 	struct ipw2200_rateset		rs;
7933847Seh146360 	struct ipw2200_wep_key		wkey;
7943847Seh146360 	int				err, i;
7953847Seh146360 
7963847Seh146360 	/*
7973847Seh146360 	 * Set the IBSS mode channel: Tx power
7983847Seh146360 	 */
7993847Seh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
8003847Seh146360 		pwr.mode  = IPW2200_MODE_11B;
8013847Seh146360 		pwr.nchan = 11;
8023847Seh146360 		for (i = 0; i < pwr.nchan; i++) {
8033847Seh146360 			pwr.chan[i].chan  = i + 1;
8043847Seh146360 			pwr.chan[i].power = IPW2200_TXPOWER_MAX;
8053847Seh146360 		}
8063847Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8073847Seh146360 		    "ipw2200_config(): Setting .11b channels Tx power\n"));
8083847Seh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
8093847Seh146360 		    &pwr, sizeof (pwr), 0);
8103847Seh146360 		if (err != DDI_SUCCESS)
8113847Seh146360 			return (err);
8123847Seh146360 
8133847Seh146360 		pwr.mode  = IPW2200_MODE_11G;
8143847Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8153847Seh146360 		    "ipw2200_config(): Setting .11g channels Tx power\n"));
8163847Seh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
8173847Seh146360 		    &pwr, sizeof (pwr), 0);
8183847Seh146360 		if (err != DDI_SUCCESS)
8193847Seh146360 			return (err);
8203847Seh146360 	}
8213847Seh146360 
8223847Seh146360 	/*
8233847Seh146360 	 * Set MAC address
8243847Seh146360 	 */
8253847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8263847Seh146360 	    "ipw2200_config(): Setting MAC address to "
8273847Seh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
8283847Seh146360 	    ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
8293847Seh146360 	    ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]));
8303847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr,
8313847Seh146360 	    IEEE80211_ADDR_LEN, 0);
8323847Seh146360 	if (err != DDI_SUCCESS)
8333847Seh146360 		return (err);
8343847Seh146360 
8353847Seh146360 	/*
8363847Seh146360 	 * Set basic system config settings: configuration(capabilities)
8373847Seh146360 	 */
8383847Seh146360 	(void) memset(&cfg, 0, sizeof (cfg));
8393847Seh146360 	cfg.bluetooth_coexistence	 = 1;
8403847Seh146360 	cfg.multicast_enabled		 = 1;
8413847Seh146360 	cfg.answer_pbreq		 = 1;
8423847Seh146360 	cfg.noise_reported		 = 1;
8435485Seh146360 	cfg.disable_multicast_decryption = 1; /* WPA */
8445485Seh146360 	cfg.disable_unicast_decryption   = 1; /* WPA */
8453847Seh146360 
8463847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8473847Seh146360 	    "ipw2200_config(): Configuring adapter\n"));
8483847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
8493847Seh146360 	    &cfg, sizeof (cfg), 0);
8503847Seh146360 	if (err != DDI_SUCCESS)
8513847Seh146360 		return (err);
8523847Seh146360 
8533847Seh146360 	/*
8543847Seh146360 	 * Set power mode
8553847Seh146360 	 */
8563847Seh146360 	data = LE_32(IPW2200_POWER_MODE_CAM);
8573847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8583847Seh146360 	    "ipw2200_config(): Setting power mode to %u\n", LE_32(data)));
8593847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE,
8603847Seh146360 	    &data, sizeof (data), 0);
8613847Seh146360 	if (err != DDI_SUCCESS)
8623847Seh146360 		return (err);
8633847Seh146360 
8643847Seh146360 	/*
8653847Seh146360 	 * Set supported rates
8663847Seh146360 	 */
8673847Seh146360 	rs.mode = IPW2200_MODE_11G;
8683847Seh146360 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
8693847Seh146360 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates;
8703847Seh146360 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates,
8713847Seh146360 	    rs.nrates);
8723847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8733847Seh146360 	    "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates));
8743847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
8753847Seh146360 	if (err != DDI_SUCCESS)
8763847Seh146360 		return (err);
8773847Seh146360 
8783847Seh146360 	rs.mode = IPW2200_MODE_11A;
8793847Seh146360 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
8803847Seh146360 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates;
8813847Seh146360 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates,
8823847Seh146360 	    rs.nrates);
8833847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8843847Seh146360 	    "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates));
8853847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
8863847Seh146360 	if (err != DDI_SUCCESS)
8873847Seh146360 		return (err);
8883847Seh146360 
8893847Seh146360 	/*
8903847Seh146360 	 * Set RTS(request-to-send) threshold
8913847Seh146360 	 */
8923847Seh146360 	data = LE_32(ic->ic_rtsthreshold);
8933847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
8943847Seh146360 	    "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data)));
8953847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data,
8963847Seh146360 	    sizeof (data), 0);
8973847Seh146360 	if (err != DDI_SUCCESS)
8983847Seh146360 		return (err);
8993847Seh146360 
9003847Seh146360 	/*
9013847Seh146360 	 * Set fragmentation threshold
9023847Seh146360 	 */
9033847Seh146360 	data = LE_32(ic->ic_fragthreshold);
9043847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
9053847Seh146360 	    "ipw2200_config(): Setting fragmentation threshold to %u\n",
9063847Seh146360 	    LE_32(data)));
9073847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data,
9083847Seh146360 	    sizeof (data), 0);
9093847Seh146360 	if (err != DDI_SUCCESS)
9103847Seh146360 		return (err);
9113847Seh146360 
9123847Seh146360 	/*
9133847Seh146360 	 * Set desired ESSID if we have
9143847Seh146360 	 */
9153847Seh146360 	if (ic->ic_des_esslen != 0) {
9163847Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
9173847Seh146360 		    "ipw2200_config(): Setting desired ESSID to "
9183847Seh146360 		    "(%u),%c%c%c%c%c%c%c%c\n",
9193847Seh146360 		    ic->ic_des_esslen,
9203847Seh146360 		    ic->ic_des_essid[0], ic->ic_des_essid[1],
9213847Seh146360 		    ic->ic_des_essid[2], ic->ic_des_essid[3],
9223847Seh146360 		    ic->ic_des_essid[4], ic->ic_des_essid[5],
9233847Seh146360 		    ic->ic_des_essid[6], ic->ic_des_essid[7]));
9243847Seh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid,
9253847Seh146360 		    ic->ic_des_esslen, 0);
9263847Seh146360 		if (err != DDI_SUCCESS)
9273847Seh146360 			return (err);
9283847Seh146360 	}
9293847Seh146360 
9303847Seh146360 	/*
9313847Seh146360 	 * Set WEP initial vector(random seed)
9323847Seh146360 	 */
9333847Seh146360 	(void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data));
9343847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
9353847Seh146360 	    "ipw2200_config(): Setting initialization vector to %u\n",
9363847Seh146360 	    LE_32(data)));
9373847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0);
9383847Seh146360 	if (err != DDI_SUCCESS)
9393847Seh146360 		return (err);
9403847Seh146360 
9413847Seh146360 	/*
9423847Seh146360 	 * Set WEP if any
9433847Seh146360 	 */
9443847Seh146360 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
9453847Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
9463847Seh146360 		    "ipw2200_config(): Setting Wep Key\n", LE_32(data)));
9473847Seh146360 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
9483847Seh146360 			wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY;
9493847Seh146360 			wkey.idx = (uint8_t)i;
9503847Seh146360 			wkey.len = ic->ic_nw_keys[i].wk_keylen;
9513847Seh146360 			(void) memset(wkey.key, 0, sizeof (wkey.key));
9523847Seh146360 			if (ic->ic_nw_keys[i].wk_keylen)
9533847Seh146360 				(void) memcpy(wkey.key,
9543847Seh146360 				    ic->ic_nw_keys[i].wk_key,
9553847Seh146360 				    ic->ic_nw_keys[i].wk_keylen);
9563847Seh146360 			err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY,
9573847Seh146360 			    &wkey, sizeof (wkey), 0);
9583847Seh146360 			if (err != DDI_SUCCESS)
9593847Seh146360 				return (err);
9603847Seh146360 		}
9613847Seh146360 	}
9623847Seh146360 
9633847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
9643847Seh146360 	    "ipw2200_config(): Enabling adapter\n"));
9653847Seh146360 
9663847Seh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0));
9673847Seh146360 }
9683847Seh146360 
9693847Seh146360 static int
ipw2200_cmd(struct ipw2200_softc * sc,uint32_t type,void * buf,size_t len,int async)9703847Seh146360 ipw2200_cmd(struct ipw2200_softc *sc,
9713847Seh146360 	uint32_t type, void *buf, size_t len, int async)
9723847Seh146360 {
9733847Seh146360 	struct		ipw2200_cmd_desc *cmd;
9743847Seh146360 	clock_t		clk;
9753847Seh146360 	uint32_t	idx;
9763847Seh146360 
9773847Seh146360 	mutex_enter(&sc->sc_cmd_lock);
9783847Seh146360 	while (sc->sc_cmd_free < 1)
9793847Seh146360 		cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock);
9803847Seh146360 
9813847Seh146360 	idx = sc->sc_cmd_cur;
9823847Seh146360 	cmd = &sc->sc_cmdsc[idx];
9833847Seh146360 	(void) memset(cmd, 0, sizeof (*cmd));
9843847Seh146360 
9853847Seh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
9863847Seh146360 	    "ipw2200_cmd(): cmd-cur=%d\n", idx));
9873847Seh146360 
9883847Seh146360 	cmd->hdr.type   = IPW2200_HDR_TYPE_COMMAND;
9893847Seh146360 	cmd->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
9903847Seh146360 	cmd->type	= (uint8_t)type;
9913847Seh146360 	if (len == 0 || buf == NULL)
9923847Seh146360 		cmd->len  = 0;
9933847Seh146360 	else {
9943847Seh146360 		cmd->len  = (uint8_t)len;
9953847Seh146360 		(void) memcpy(cmd->data, buf, len);
9963847Seh146360 	}
9973847Seh146360 	sc->sc_done[idx] = 0;
9983847Seh146360 
9993847Seh146360 	/*
10003847Seh146360 	 * DMA sync
10013847Seh146360 	 */
10023847Seh146360 	(void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd,
10033847Seh146360 	    idx * sizeof (struct ipw2200_cmd_desc),
10043847Seh146360 	    sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV);
10053847Seh146360 
10063847Seh146360 	sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE);
10073847Seh146360 	sc->sc_cmd_free--;
10083847Seh146360 
10093847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
10103847Seh146360 
10113847Seh146360 	mutex_exit(&sc->sc_cmd_lock);
10123847Seh146360 
10133847Seh146360 	if (async)
10143847Seh146360 		goto out;
10153847Seh146360 
10163847Seh146360 	/*
10173847Seh146360 	 * Wait for command done
10183847Seh146360 	 */
101911066Srafael.vanoni@sun.com 	clk = drv_usectohz(5000000);
10203847Seh146360 	mutex_enter(&sc->sc_ilock);
10213847Seh146360 	while (sc->sc_done[idx] == 0) {
10223847Seh146360 		/* pending */
102311066Srafael.vanoni@sun.com 		if (cv_reltimedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock,
102411066Srafael.vanoni@sun.com 		    clk, TR_CLOCK_TICK) < 0)
10253847Seh146360 			break;
10263847Seh146360 	}
10273847Seh146360 	mutex_exit(&sc->sc_ilock);
10283847Seh146360 
10293847Seh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
10303847Seh146360 	    "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no"));
10313847Seh146360 
10323847Seh146360 	if (sc->sc_done[idx] == 0)
10333847Seh146360 		return (DDI_FAILURE);
10343847Seh146360 
10353847Seh146360 out:
10363847Seh146360 	return (DDI_SUCCESS);
10373847Seh146360 }
10383847Seh146360 
10393847Seh146360 /*
10403847Seh146360  * If init failed, it will call stop internally. Therefore, it's unnecessary
10413847Seh146360  * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may
10423847Seh146360  * be called twice.
10433847Seh146360  */
10443847Seh146360 int
ipw2200_init(struct ipw2200_softc * sc)10453847Seh146360 ipw2200_init(struct ipw2200_softc *sc)
10463847Seh146360 {
10473847Seh146360 	int	err;
10483847Seh146360 
10493847Seh146360 	/*
10503847Seh146360 	 * No firmware is available, failed
10513847Seh146360 	 */
10523847Seh146360 	if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) {
10533847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
10543847Seh146360 		    "ipw2200_init(): no firmware is available\n"));
10553847Seh146360 		return (DDI_FAILURE); /* return directly */
10563847Seh146360 	}
10573847Seh146360 
10583847Seh146360 	ipw2200_stop(sc);
10593847Seh146360 
10603847Seh146360 	err = ipw2200_chip_reset(sc);
10613847Seh146360 	if (err != DDI_SUCCESS) {
10623847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
10633847Seh146360 		    "ipw2200_init(): could not reset adapter\n"));
10643847Seh146360 		goto fail;
10653847Seh146360 	}
10663847Seh146360 
10673847Seh146360 	/*
10683847Seh146360 	 * Load boot code
10693847Seh146360 	 */
10703847Seh146360 	err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size);
10713847Seh146360 	if (err != DDI_SUCCESS) {
10723847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
10733847Seh146360 		    "ipw2200_init(): could not load boot code\n"));
10743847Seh146360 		goto fail;
10753847Seh146360 	}
10763847Seh146360 
10773847Seh146360 	/*
10783847Seh146360 	 * Load boot microcode
10793847Seh146360 	 */
10803847Seh146360 	err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size);
10813847Seh146360 	if (err != DDI_SUCCESS) {
10823847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
10833847Seh146360 		    "ipw2200_init(): could not load microcode\n"));
10843847Seh146360 		goto fail;
10853847Seh146360 	}
10863847Seh146360 
10873847Seh146360 	ipw2200_master_stop(sc);
10883847Seh146360 	ipw2200_ring_hwsetup(sc);
10893847Seh146360 
10903847Seh146360 	/*
10913847Seh146360 	 * Load firmware
10923847Seh146360 	 */
10933847Seh146360 	err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size);
10943847Seh146360 	if (err != DDI_SUCCESS) {
10953847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
10963847Seh146360 		    "ipw2200_init(): could not load firmware\n"));
10973847Seh146360 		goto fail;
10983847Seh146360 	}
10993847Seh146360 
11003847Seh146360 	sc->sc_flags |= IPW2200_FLAG_FW_INITED;
11013847Seh146360 
11023847Seh146360 	/*
11033847Seh146360 	 * Hardware will be enabled after configuration
11043847Seh146360 	 */
11053847Seh146360 	err = ipw2200_config(sc);
11063847Seh146360 	if (err != DDI_SUCCESS) {
11073847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
11083847Seh146360 		    "ipw2200_init(): device configuration failed\n"));
11093847Seh146360 		goto fail;
11103847Seh146360 	}
11113847Seh146360 
11123847Seh146360 	/*
11133847Seh146360 	 * workround to prevent too many h/w error.
11143847Seh146360 	 * delay for a while till h/w is stable.
11153847Seh146360 	 */
11163847Seh146360 	delay(drv_usectohz(delay_config_stable));
11173847Seh146360 
11183847Seh146360 	return (DDI_SUCCESS); /* return successfully */
11193847Seh146360 fail:
11203847Seh146360 	ipw2200_stop(sc);
11213847Seh146360 	return (err);
11223847Seh146360 }
11233847Seh146360 
11243847Seh146360 /*
11253847Seh146360  * get hardware configurations from EEPROM embedded within PRO/2200
11263847Seh146360  */
11273847Seh146360 static void
ipw2200_hwconf_get(struct ipw2200_softc * sc)11283847Seh146360 ipw2200_hwconf_get(struct ipw2200_softc *sc)
11293847Seh146360 {
11303847Seh146360 	int		i;
11313847Seh146360 	uint16_t	val;
11323847Seh146360 
11333847Seh146360 	/*
11343847Seh146360 	 * Get mac address
11353847Seh146360 	 */
11363847Seh146360 	i = 0;
11373847Seh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0);
11383847Seh146360 	sc->sc_macaddr[i++] = val >> 8;
11393847Seh146360 	sc->sc_macaddr[i++] = val & 0xff;
11403847Seh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1);
11413847Seh146360 	sc->sc_macaddr[i++] = val >> 8;
11423847Seh146360 	sc->sc_macaddr[i++] = val & 0xff;
11433847Seh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2);
11443847Seh146360 	sc->sc_macaddr[i++] = val >> 8;
11453847Seh146360 	sc->sc_macaddr[i++] = val & 0xff;
11463847Seh146360 
11473847Seh146360 	/*
11483847Seh146360 	 * formatted MAC address string
11493847Seh146360 	 */
11503847Seh146360 	(void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr),
11513847Seh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x",
11523847Seh146360 	    sc->sc_macaddr[0], sc->sc_macaddr[1],
11533847Seh146360 	    sc->sc_macaddr[2], sc->sc_macaddr[3],
11543847Seh146360 	    sc->sc_macaddr[4], sc->sc_macaddr[5]);
11553847Seh146360 
11563847Seh146360 }
11573847Seh146360 
11583847Seh146360 /*
11593847Seh146360  * all ipw2200 interrupts will be masked by this routine
11603847Seh146360  */
11613847Seh146360 static void
ipw2200_master_stop(struct ipw2200_softc * sc)11623847Seh146360 ipw2200_master_stop(struct ipw2200_softc *sc)
11633847Seh146360 {
11643847Seh146360 	int	ntries;
11653847Seh146360 
11663847Seh146360 	/*
11673847Seh146360 	 * disable interrupts
11683847Seh146360 	 */
11693847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
11703847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER);
11713847Seh146360 
11723847Seh146360 	/*
11733847Seh146360 	 * wait long enough to ensure hardware stop successfully.
11743847Seh146360 	 */
11753847Seh146360 	for (ntries = 0; ntries < 500; ntries++) {
11763847Seh146360 		if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) &
11773847Seh146360 		    IPW2200_RST_MASTER_DISABLED)
11783847Seh146360 			break;
11793847Seh146360 		/* wait for a while */
11803847Seh146360 		drv_usecwait(100);
11813847Seh146360 	}
11828165SFei.Feng@Sun.COM 	if (ntries == 500)
11833847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
11843847Seh146360 		    "ipw2200_master_stop(): timeout\n"));
11853847Seh146360 
11863847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
11873847Seh146360 	    IPW2200_RST_PRINCETON_RESET |
11883847Seh146360 	    ipw2200_csr_get32(sc, IPW2200_CSR_RST));
11893847Seh146360 
11903847Seh146360 	sc->sc_flags &= ~IPW2200_FLAG_FW_INITED;
11913847Seh146360 }
11923847Seh146360 
11933847Seh146360 /*
11943847Seh146360  * all ipw2200 interrupts will be masked by this routine
11953847Seh146360  */
11963847Seh146360 static int
ipw2200_chip_reset(struct ipw2200_softc * sc)11973847Seh146360 ipw2200_chip_reset(struct ipw2200_softc *sc)
11983847Seh146360 {
11993847Seh146360 	uint32_t	tmp;
12003847Seh146360 	int		ntries, i;
12013847Seh146360 
12023847Seh146360 	ipw2200_master_stop(sc);
12033847Seh146360 
12043847Seh146360 	/*
12053847Seh146360 	 * Move adapter to DO state
12063847Seh146360 	 */
12073847Seh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
12083847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
12093847Seh146360 
12103847Seh146360 	/*
12113847Seh146360 	 * Initialize Phase-Locked Level (PLL)
12123847Seh146360 	 */
12133847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST);
12143847Seh146360 
12153847Seh146360 	/*
12163847Seh146360 	 * Wait for clock stabilization
12173847Seh146360 	 */
12183847Seh146360 	for (ntries = 0; ntries < 1000; ntries++) {
12193847Seh146360 		if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) &
12203847Seh146360 		    IPW2200_CTL_CLOCK_READY)
12213847Seh146360 			break;
12223847Seh146360 		drv_usecwait(200);
12233847Seh146360 	}
12243847Seh146360 	if (ntries == 1000) {
12253847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
12263847Seh146360 		    "ipw2200_chip_reset(): timeout\n"));
12273847Seh146360 		return (DDI_FAILURE);
12283847Seh146360 	}
12293847Seh146360 
12303847Seh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST);
12313847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET);
12323847Seh146360 
12333847Seh146360 	drv_usecwait(10);
12343847Seh146360 
12353847Seh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
12363847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
12373847Seh146360 
12383847Seh146360 	/*
12393847Seh146360 	 * clear NIC memory
12403847Seh146360 	 */
12413847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0);
12423847Seh146360 	for (i = 0; i < 0xc000; i++)
12433847Seh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0);
12443847Seh146360 
12453847Seh146360 	return (DDI_SUCCESS);
12463847Seh146360 }
12473847Seh146360 
12483847Seh146360 /*
12493847Seh146360  * This function is used by wificonfig/dladm to get the current
12503847Seh146360  * radio status, it is off/on
12513847Seh146360  */
12523847Seh146360 int
ipw2200_radio_status(struct ipw2200_softc * sc)12533847Seh146360 ipw2200_radio_status(struct ipw2200_softc *sc)
12543847Seh146360 {
12553847Seh146360 	int	val;
12563847Seh146360 
12573847Seh146360 	val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) &
12583847Seh146360 	    IPW2200_IO_RADIO_ENABLED) ? 1 : 0;
12593847Seh146360 
12603847Seh146360 	return (val);
12613847Seh146360 }
12623847Seh146360 /*
12633847Seh146360  * This function is used to get the statistic
12643847Seh146360  */
12653847Seh146360 void
ipw2200_get_statistics(struct ipw2200_softc * sc)12663847Seh146360 ipw2200_get_statistics(struct ipw2200_softc *sc)
12673847Seh146360 {
12683847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
12693847Seh146360 
12703847Seh146360 	uint32_t size, buf[128];
12713847Seh146360 
12723847Seh146360 	if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) {
12733847Seh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
12743847Seh146360 		    "ipw2200_get_statistic(): fw doesn't download yet."));
12753847Seh146360 		return;
12763847Seh146360 	}
12773847Seh146360 
12783847Seh146360 	size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1);
12793847Seh146360 	ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size);
12803847Seh146360 
12813847Seh146360 	/*
12823847Seh146360 	 * To retrieve the statistic information into proper places. There are
12833847Seh146360 	 * lot of information. These table will be read once a second.
12843847Seh146360 	 * Hopefully, it will not effect the performance.
12853847Seh146360 	 */
12863847Seh146360 
12873847Seh146360 	/*
12883847Seh146360 	 * For the tx/crc information, we can get them from chip directly;
12893847Seh146360 	 * For the rx/wep error/(rts) related information, leave them net80211.
12903847Seh146360 	 */
12913847Seh146360 	/* WIFI_STAT_TX_FRAGS */
12923847Seh146360 	ic->ic_stats.is_tx_frags = (uint32_t)buf[5];
12933847Seh146360 	/* WIFI_STAT_MCAST_TX */
12943847Seh146360 	ic->ic_stats.is_tx_mcast = (uint32_t)buf[31];
12953847Seh146360 	/* WIFI_STAT_TX_RETRANS */
12963847Seh146360 	ic->ic_stats.is_tx_retries = (uint32_t)buf[56];
12973847Seh146360 	/* WIFI_STAT_TX_FAILED */
12983847Seh146360 	ic->ic_stats.is_tx_failed = (uint32_t)buf[57];
12993847Seh146360 	/* MAC_STAT_OBYTES */
13003847Seh146360 	ic->ic_stats.is_tx_bytes = (uint32_t)buf[64];
13013847Seh146360 }
13023847Seh146360 
13033847Seh146360 /*
13043847Seh146360  * DMA region alloc subroutine
13053847Seh146360  */
13063847Seh146360 int
ipw2200_dma_region_alloc(struct ipw2200_softc * sc,struct dma_region * dr,size_t size,uint_t dir,uint_t flags)13073847Seh146360 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr,
13083847Seh146360 	size_t size, uint_t dir, uint_t flags)
13093847Seh146360 {
13103847Seh146360 	dev_info_t	*dip = sc->sc_dip;
13113847Seh146360 	int		err;
13123847Seh146360 
13133847Seh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
13143847Seh146360 	    "ipw2200_dma_region_alloc(): size =%u\n", size));
13153847Seh146360 
13163847Seh146360 	err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL,
13173847Seh146360 	    &dr->dr_hnd);
13183847Seh146360 	if (err != DDI_SUCCESS) {
13193847Seh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
13203847Seh146360 		    "ipw2200_dma_region_alloc(): "
13213847Seh146360 		    "ddi_dma_alloc_handle() failed\n"));
13223847Seh146360 		goto fail0;
13233847Seh146360 	}
13243847Seh146360 
13253847Seh146360 	err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr,
13263847Seh146360 	    flags, DDI_DMA_SLEEP, NULL,
13273847Seh146360 	    &dr->dr_base, &dr->dr_size, &dr->dr_acc);
13283847Seh146360 	if (err != DDI_SUCCESS) {
13293847Seh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
13303847Seh146360 		    "ipw2200_dma_region_alloc(): "
13313847Seh146360 		    "ddi_dma_mem_alloc() failed\n"));
13323847Seh146360 		goto fail1;
13333847Seh146360 	}
13343847Seh146360 
13353847Seh146360 	err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL,
13363847Seh146360 	    dr->dr_base, dr->dr_size,
13373847Seh146360 	    dir | flags, DDI_DMA_SLEEP, NULL,
13383847Seh146360 	    &dr->dr_cookie, &dr->dr_ccnt);
13393847Seh146360 	if (err != DDI_DMA_MAPPED) {
13403847Seh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
13413847Seh146360 		    "ipw2200_dma_region_alloc(): "
13423847Seh146360 		    "ddi_dma_addr_bind_handle() failed\n"));
13433847Seh146360 		goto fail2;
13443847Seh146360 	}
13453847Seh146360 
13463847Seh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
13473847Seh146360 	    "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt));
13483847Seh146360 
13493847Seh146360 	if (dr->dr_ccnt != 1) {
13503847Seh146360 		err = DDI_FAILURE;
13513847Seh146360 		goto fail3;
13523847Seh146360 	}
13533847Seh146360 
13543847Seh146360 	dr->dr_pbase = dr->dr_cookie.dmac_address;
13553847Seh146360 
13563847Seh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
13573847Seh146360 	    "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n",
13583847Seh146360 	    dr->dr_pbase));
13593847Seh146360 
13603847Seh146360 	return (DDI_SUCCESS);
13613847Seh146360 
13623847Seh146360 fail3:
13633847Seh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
13643847Seh146360 fail2:
13653847Seh146360 	ddi_dma_mem_free(&dr->dr_acc);
13663847Seh146360 fail1:
13673847Seh146360 	ddi_dma_free_handle(&dr->dr_hnd);
13683847Seh146360 fail0:
13693847Seh146360 	return (err);
13703847Seh146360 }
13713847Seh146360 
13723847Seh146360 void
ipw2200_dma_region_free(struct dma_region * dr)13733847Seh146360 ipw2200_dma_region_free(struct dma_region *dr)
13743847Seh146360 {
13753847Seh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
13763847Seh146360 	ddi_dma_mem_free(&dr->dr_acc);
13773847Seh146360 	ddi_dma_free_handle(&dr->dr_hnd);
13783847Seh146360 }
13793847Seh146360 
13803847Seh146360 static int
ipw2200_ring_alloc(struct ipw2200_softc * sc)13813847Seh146360 ipw2200_ring_alloc(struct ipw2200_softc *sc)
13823847Seh146360 {
13833847Seh146360 	int	err, i;
13843847Seh146360 
13853847Seh146360 	/*
13863847Seh146360 	 * tx desc ring
13873847Seh146360 	 */
13883847Seh146360 	sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring";
13893847Seh146360 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc,
13903847Seh146360 	    IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc),
13913847Seh146360 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
13923847Seh146360 	if (err != DDI_SUCCESS)
13933847Seh146360 		goto fail0;
13943847Seh146360 	/*
13953847Seh146360 	 * tx buffer array
13963847Seh146360 	 */
13973847Seh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++) {
13983847Seh146360 		sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf";
13993847Seh146360 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i],
14003847Seh146360 		    IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING);
14013847Seh146360 		if (err != DDI_SUCCESS) {
14023847Seh146360 			while (i >= 0) {
14033847Seh146360 				ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
14043847Seh146360 				i--;
14053847Seh146360 			}
14063847Seh146360 			goto fail1;
14073847Seh146360 		}
14083847Seh146360 	}
14093847Seh146360 	/*
14103847Seh146360 	 * rx buffer array
14113847Seh146360 	 */
14123847Seh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++) {
14133847Seh146360 		sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf";
14143847Seh146360 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i],
14153847Seh146360 		    IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING);
14163847Seh146360 		if (err != DDI_SUCCESS) {
14173847Seh146360 			while (i >= 0) {
14183847Seh146360 				ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
14193847Seh146360 				i--;
14203847Seh146360 			}
14213847Seh146360 			goto fail2;
14223847Seh146360 		}
14233847Seh146360 	}
14243847Seh146360 	/*
14253847Seh146360 	 * cmd desc ring
14263847Seh146360 	 */
14273847Seh146360 	sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring";
14283847Seh146360 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc,
14293847Seh146360 	    IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc),
14303847Seh146360 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
14313847Seh146360 	if (err != DDI_SUCCESS)
14323847Seh146360 		goto fail3;
14333847Seh146360 
14343847Seh146360 	return (DDI_SUCCESS);
14353847Seh146360 
14363847Seh146360 fail3:
14373847Seh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
14383847Seh146360 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
14393847Seh146360 fail2:
14403847Seh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
14413847Seh146360 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
14423847Seh146360 fail1:
14433847Seh146360 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
14443847Seh146360 fail0:
14453847Seh146360 	return (err);
14463847Seh146360 }
14473847Seh146360 
14483847Seh146360 static void
ipw2200_ring_free(struct ipw2200_softc * sc)14493847Seh146360 ipw2200_ring_free(struct ipw2200_softc *sc)
14503847Seh146360 {
14513847Seh146360 	int	i;
14523847Seh146360 
14533847Seh146360 	/*
14543847Seh146360 	 * tx ring desc
14553847Seh146360 	 */
14563847Seh146360 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
14573847Seh146360 	/*
14583847Seh146360 	 * tx buf
14593847Seh146360 	 */
14603847Seh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
14613847Seh146360 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
14623847Seh146360 	/*
14633847Seh146360 	 * rx buf
14643847Seh146360 	 */
14653847Seh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
14663847Seh146360 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
14673847Seh146360 	/*
14683847Seh146360 	 * command ring desc
14693847Seh146360 	 */
14703847Seh146360 	ipw2200_dma_region_free(&sc->sc_dma_cmdsc);
14713847Seh146360 }
14723847Seh146360 
14733847Seh146360 static void
ipw2200_ring_reset(struct ipw2200_softc * sc)14743847Seh146360 ipw2200_ring_reset(struct ipw2200_softc *sc)
14753847Seh146360 {
14763847Seh146360 	int i;
14773847Seh146360 
14783847Seh146360 	/*
14793847Seh146360 	 * tx desc ring & buffer array
14803847Seh146360 	 */
14813847Seh146360 	sc->sc_tx_cur   = 0;
14823847Seh146360 	sc->sc_tx_free  = IPW2200_TX_RING_SIZE;
14833847Seh146360 	sc->sc_txdsc    = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base;
14843847Seh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
14853847Seh146360 		sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base;
14863847Seh146360 	/*
14873847Seh146360 	 * rx buffer array
14883847Seh146360 	 */
14893847Seh146360 	sc->sc_rx_cur   = 0;
14903847Seh146360 	sc->sc_rx_free  = IPW2200_RX_RING_SIZE;
14913847Seh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
14923847Seh146360 		sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base;
14933847Seh146360 
14943847Seh146360 	/*
14953847Seh146360 	 * command desc ring
14963847Seh146360 	 */
14973847Seh146360 	sc->sc_cmd_cur  = 0;
14983847Seh146360 	sc->sc_cmd_free = IPW2200_CMD_RING_SIZE;
14993847Seh146360 	sc->sc_cmdsc    = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base;
15003847Seh146360 }
15013847Seh146360 
15023847Seh146360 /*
15033847Seh146360  * tx, rx rings and command initialization
15043847Seh146360  */
15053847Seh146360 static int
ipw2200_ring_init(struct ipw2200_softc * sc)15063847Seh146360 ipw2200_ring_init(struct ipw2200_softc *sc)
15073847Seh146360 {
15083847Seh146360 	int	err;
15093847Seh146360 
15103847Seh146360 	err = ipw2200_ring_alloc(sc);
15113847Seh146360 	if (err != DDI_SUCCESS)
15123847Seh146360 		return (err);
15133847Seh146360 
15143847Seh146360 	ipw2200_ring_reset(sc);
15153847Seh146360 
15163847Seh146360 	return (DDI_SUCCESS);
15173847Seh146360 }
15183847Seh146360 
15193847Seh146360 static void
ipw2200_ring_hwsetup(struct ipw2200_softc * sc)15203847Seh146360 ipw2200_ring_hwsetup(struct ipw2200_softc *sc)
15213847Seh146360 {
15223847Seh146360 	int	i;
15233847Seh146360 
15243847Seh146360 	/*
15253847Seh146360 	 * command desc ring
15263847Seh146360 	 */
15273847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase);
15283847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE);
15293847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
15303847Seh146360 
15313847Seh146360 	/*
15323847Seh146360 	 * tx desc ring.  only tx1 is used, tx2, tx3, and tx4 are unused
15333847Seh146360 	 */
15343847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase);
15353847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE);
15363847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
15373847Seh146360 
15383847Seh146360 	/*
15393847Seh146360 	 * tx2, tx3, tx4 is not used
15403847Seh146360 	 */
15413847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase);
15423847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE);
15433847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0);
15443847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0);
15453847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase);
15463847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE);
15473847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0);
15483847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0);
15493847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase);
15503847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE);
15513847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0);
15523847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0);
15533847Seh146360 
15543847Seh146360 	/*
15553847Seh146360 	 * rx buffer ring
15563847Seh146360 	 */
15573847Seh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
15583847Seh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4,
15593847Seh146360 		    sc->sc_dma_rxbufs[i].dr_pbase);
15603847Seh146360 	/*
15613847Seh146360 	 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
15623847Seh146360 	 */
15633847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
15643847Seh146360 	    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE));
15653847Seh146360 }
15663847Seh146360 
15673847Seh146360 int
ipw2200_start_scan(struct ipw2200_softc * sc)15683847Seh146360 ipw2200_start_scan(struct ipw2200_softc *sc)
15693847Seh146360 {
15703847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
15713847Seh146360 	struct ipw2200_scan	scan;
15723847Seh146360 	uint8_t			*ch;
15733847Seh146360 	int			cnt, i;
15743847Seh146360 
15753847Seh146360 	IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
15763847Seh146360 	    "ipw2200_start_scan(): start scanning \n"));
15773847Seh146360 
15783847Seh146360 	/*
15793847Seh146360 	 * start scanning
15803847Seh146360 	 */
15813847Seh146360 	sc->sc_flags |= IPW2200_FLAG_SCANNING;
15823847Seh146360 
15833847Seh146360 	(void) memset(&scan, 0, sizeof (scan));
15843847Seh146360 	scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED :
15853847Seh146360 	    IPW2200_SCAN_TYPE_BROADCAST;
15863847Seh146360 	scan.dwelltime = LE_16(40); /* The interval is set up to 40 */
15873847Seh146360 
15883847Seh146360 	/*
15893847Seh146360 	 * Compact supported channel number(5G) into a single buffer
15903847Seh146360 	 */
15913847Seh146360 	ch = scan.channels;
15923847Seh146360 	cnt = 0;
15933847Seh146360 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
15943847Seh146360 		if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) &&
15953847Seh146360 		    isset(ic->ic_chan_active, i)) {
15963847Seh146360 			*++ch = (uint8_t)i;
15973847Seh146360 			cnt++;
15983847Seh146360 		}
15993847Seh146360 	}
16003847Seh146360 	*(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt;
16014103Sql147931 	ch = (cnt > 0) ? (ch + 1) : (scan.channels);
16023847Seh146360 
16033847Seh146360 	/*
16043847Seh146360 	 * Compact supported channel number(2G) into a single buffer
16053847Seh146360 	 */
16063847Seh146360 	cnt = 0;
16073847Seh146360 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
16083847Seh146360 		if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) &&
16093847Seh146360 		    isset(ic->ic_chan_active, i)) {
16103847Seh146360 			*++ch = (uint8_t)i;
16113847Seh146360 			cnt++;
16123847Seh146360 		}
16133847Seh146360 	}
16143847Seh146360 	*(ch - cnt) = IPW2200_CHAN_2GHZ | cnt;
16153847Seh146360 
16163847Seh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1));
16173847Seh146360 }
16183847Seh146360 
16193847Seh146360 int
ipw2200_auth_and_assoc(struct ipw2200_softc * sc)16203847Seh146360 ipw2200_auth_and_assoc(struct ipw2200_softc *sc)
16213847Seh146360 {
16223847Seh146360 	struct ieee80211com		*ic = &sc->sc_ic;
16233847Seh146360 	struct ieee80211_node		*in = ic->ic_bss;
16243847Seh146360 	struct ipw2200_configuration	cfg;
16253847Seh146360 	struct ipw2200_rateset		rs;
16263847Seh146360 	struct ipw2200_associate	assoc;
16273847Seh146360 	uint32_t			data;
16283847Seh146360 	int				err;
16295485Seh146360 	uint8_t				*wpa_level;
16305485Seh146360 
16315485Seh146360 	if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED) {
16325485Seh146360 		/* already associated */
16335485Seh146360 		return (-1);
16345485Seh146360 	}
16353847Seh146360 
16363847Seh146360 	/*
16373847Seh146360 	 * set the confiuration
16383847Seh146360 	 */
16393847Seh146360 	if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) {
16403847Seh146360 		/* enable b/g auto-detection */
16413847Seh146360 		(void) memset(&cfg, 0, sizeof (cfg));
16423847Seh146360 		cfg.bluetooth_coexistence = 1;
16433847Seh146360 		cfg.multicast_enabled	  = 1;
16443847Seh146360 		cfg.use_protection	  = 1;
16453847Seh146360 		cfg.answer_pbreq	  = 1;
16463847Seh146360 		cfg.noise_reported	  = 1;
16475485Seh146360 		cfg.disable_multicast_decryption = 1; /* WPA */
16485485Seh146360 		cfg.disable_unicast_decryption   = 1; /* WPA */
16493847Seh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
16503847Seh146360 		    &cfg, sizeof (cfg), 1);
16513847Seh146360 		if (err != DDI_SUCCESS)
16523847Seh146360 			return (err);
16533847Seh146360 	}
16543847Seh146360 
16553847Seh146360 	/*
16563847Seh146360 	 * set the essid, may be null/hidden AP
16573847Seh146360 	 */
16583847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
16593847Seh146360 	    "ipw2200_auth_and_assoc(): "
16603847Seh146360 	    "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n",
16613847Seh146360 	    in->in_esslen,
16623847Seh146360 	    in->in_essid[0], in->in_essid[1],
16633847Seh146360 	    in->in_essid[2], in->in_essid[3],
16643847Seh146360 	    in->in_essid[4], in->in_essid[5],
16653847Seh146360 	    in->in_essid[6], in->in_essid[7]));
16663847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid,
16673847Seh146360 	    in->in_esslen, 1);
16683847Seh146360 	if (err != DDI_SUCCESS)
16693847Seh146360 		return (err);
16703847Seh146360 
16713847Seh146360 	/*
16723847Seh146360 	 * set the rate: the rate set has already been ''negocitated''
16733847Seh146360 	 */
16743847Seh146360 	rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
16753847Seh146360 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
16763847Seh146360 	rs.type = IPW2200_RATESET_TYPE_NEGOCIATED;
16773847Seh146360 	rs.nrates = in->in_rates.ir_nrates;
16783847Seh146360 	(void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates);
16793847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
16803847Seh146360 	    "ipw2200_auth_and_assoc(): "
16813847Seh146360 	    "setting negotiated rates to(nrates = %u)\n", rs.nrates));
16823847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1);
16833847Seh146360 	if (err != DDI_SUCCESS)
16843847Seh146360 		return (err);
16853847Seh146360 
16863847Seh146360 	/*
16875485Seh146360 	 * invoke command associate
16885485Seh146360 	 */
16895485Seh146360 	(void) memset(&assoc, 0, sizeof (assoc));
16905485Seh146360 
16915485Seh146360 	/*
16925485Seh146360 	 * set opt_ie to h/w if associated is WPA, opt_ie has been verified
16935485Seh146360 	 * by net80211 kernel module.
16945485Seh146360 	 */
16955485Seh146360 	if (ic->ic_opt_ie != NULL) {
16965485Seh146360 
16975485Seh146360 		wpa_level = (uint8_t *)ic->ic_opt_ie;
16985485Seh146360 
16995485Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
17005485Seh146360 		    "ipw2200_auth_and_assoc(): "
17015485Seh146360 		    "set wpa_ie and wpa_ie_len to h/w. "
17025485Seh146360 		    "length is %d\n"
17035485Seh146360 		    "opt_ie[0] = %02X - element vendor\n"
17045485Seh146360 		    "opt_ie[1] = %02X - length\n"
17055485Seh146360 		    "opt_ie[2,3,4] = %02X %02X %02X - oui\n"
17065485Seh146360 		    "opt_ie[5] = %02X - oui type\n"
17075485Seh146360 		    "opt_ie[6,7] = %02X %02X - spec version \n"
17085485Seh146360 		    "opt_ie[8,9,10,11] = %02X %02X %02X %02X - gk cipher\n"
17095485Seh146360 		    "opt_ie[12,13] = %02X %02X - pairwise key cipher(1)\n"
17105485Seh146360 		    "opt_ie[14,15,16,17] = %02X %02X %02X %02X - ciphers\n"
17115485Seh146360 		    "opt_ie[18,19] = %02X %02X - authselcont(1) \n"
17125485Seh146360 		    "opt_ie[20,21,22,23] = %02X %02X %02X %02X - authsels\n",
17135485Seh146360 		    wpa_level[1], wpa_level[0], wpa_level[1],
17145485Seh146360 		    wpa_level[2], wpa_level[3], wpa_level[4],
17155485Seh146360 		    wpa_level[5], wpa_level[6], wpa_level[7],
17165485Seh146360 		    wpa_level[8], wpa_level[9], wpa_level[10],
17175485Seh146360 		    wpa_level[11], wpa_level[12], wpa_level[13],
17185485Seh146360 		    wpa_level[14], wpa_level[15], wpa_level[16],
17195485Seh146360 		    wpa_level[17], wpa_level[18], wpa_level[19],
17205485Seh146360 		    wpa_level[20], wpa_level[21], wpa_level[22],
17215485Seh146360 		    wpa_level[23]));
17225485Seh146360 
17235485Seh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_OPTIE,
17245485Seh146360 		    ic->ic_opt_ie, ic->ic_opt_ie_len, 1);
17255485Seh146360 		if (err != DDI_SUCCESS)
17265485Seh146360 			return (err);
17275485Seh146360 	}
17285485Seh146360 
17295485Seh146360 	/*
17303847Seh146360 	 * set the sensitive
17313847Seh146360 	 */
17323847Seh146360 	data = LE_32(in->in_rssi);
17333847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
17343847Seh146360 	    "ipw2200_auth_and_assoc(): "
17353847Seh146360 	    "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi));
17363847Seh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY,
17373847Seh146360 	    &data, sizeof (data), 1);
17383847Seh146360 	if (err != DDI_SUCCESS)
17393847Seh146360 		return (err);
17403847Seh146360 
17413847Seh146360 	/*
17425485Seh146360 	 * set mode and channel for assocation command
17433847Seh146360 	 */
17443847Seh146360 	assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
17453847Seh146360 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
17463847Seh146360 	assoc.chan = ieee80211_chan2ieee(ic, in->in_chan);
17475485Seh146360 
17483847Seh146360 	/*
17493847Seh146360 	 * use the value set to ic_bss to retraive current sharedmode
17503847Seh146360 	 */
17513847Seh146360 	if (ic->ic_bss->in_authmode == WL_SHAREDKEY) {
17523847Seh146360 		assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED;
17533847Seh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
17543847Seh146360 		    "ipw2200_auth_and_assoc(): "
17553847Seh146360 		    "associate to shared key mode, set thru. ioctl"));
17563847Seh146360 	}
17575485Seh146360 
17585485Seh146360 	if (ic->ic_flags & IEEE80211_F_WPA)
17595485Seh146360 		assoc.policy = LE_16(IPW2200_POLICY_WPA); /* RSN/WPA active */
17603847Seh146360 	(void) memcpy(assoc.tstamp, in->in_tstamp.data, 8);
17613847Seh146360 	assoc.capinfo = LE_16(in->in_capinfo);
17623847Seh146360 	assoc.lintval = LE_16(ic->ic_lintval);
17633847Seh146360 	assoc.intval  = LE_16(in->in_intval);
17643847Seh146360 	IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid);
17653847Seh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS)
17663847Seh146360 		IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr);
17673847Seh146360 	else
17683847Seh146360 		IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid);
17693847Seh146360 
17703847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
17713847Seh146360 	    "ipw2200_auth_and_assoc(): "
17723847Seh146360 	    "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), "
17733847Seh146360 	    "chan(%u), auth(%u)\n",
17743847Seh146360 	    assoc.bssid[0], assoc.bssid[1], assoc.bssid[2],
17753847Seh146360 	    assoc.bssid[3], assoc.bssid[4], assoc.bssid[5],
17763847Seh146360 	    assoc.chan, assoc.auth));
17773847Seh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE,
17783847Seh146360 	    &assoc, sizeof (assoc), 1));
17793847Seh146360 }
17803847Seh146360 
17815485Seh146360 /*
17825485Seh146360  * Send the dis-association command to h/w, will receive notification to claim
17835485Seh146360  * the connection is dis-associated. So, it's not marked as disassociated this
17845485Seh146360  * moment.
17855485Seh146360  */
17865485Seh146360 static int
ipw2200_disassoc(struct ipw2200_softc * sc)17875485Seh146360 ipw2200_disassoc(struct ipw2200_softc *sc)
17885485Seh146360 {
17895485Seh146360 	struct ipw2200_associate assoc;
17905485Seh146360 	assoc.type = 2;
17915485Seh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, &assoc,
17925485Seh146360 	    sizeof (assoc), 1));
17935485Seh146360 }
17945485Seh146360 
17953847Seh146360 /* ARGSUSED */
17963847Seh146360 static int
ipw2200_newstate(struct ieee80211com * ic,enum ieee80211_state state,int arg)17973847Seh146360 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg)
17983847Seh146360 {
17993847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
18003847Seh146360 	wifi_data_t		wd = { 0 };
18013847Seh146360 
18023847Seh146360 	switch (state) {
18033847Seh146360 	case IEEE80211_S_SCAN:
18043847Seh146360 		if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) {
18053847Seh146360 			ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
18063847Seh146360 			(void) ipw2200_start_scan(sc);
18073847Seh146360 		}
18083847Seh146360 		break;
18093847Seh146360 	case IEEE80211_S_AUTH:
18105485Seh146360 		/*
18115485Seh146360 		 * The firmware will fail if we are already associated
18125485Seh146360 		 */
18135485Seh146360 		if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED)
18145485Seh146360 			(void) ipw2200_disassoc(sc);
18153847Seh146360 		(void) ipw2200_auth_and_assoc(sc);
18163847Seh146360 		break;
18173847Seh146360 	case IEEE80211_S_RUN:
18183847Seh146360 		/*
18193847Seh146360 		 * We can send data now; update the fastpath with our
18203847Seh146360 		 * current associated BSSID and other relevant settings.
18213847Seh146360 		 */
18225485Seh146360 		wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
18235485Seh146360 		wd.wd_opmode = ic->ic_opmode;
18243847Seh146360 		IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
18253847Seh146360 		(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
18263847Seh146360 		break;
18273847Seh146360 	case IEEE80211_S_ASSOC:
18283847Seh146360 	case IEEE80211_S_INIT:
18293847Seh146360 		break;
18303847Seh146360 	}
18313847Seh146360 
18323847Seh146360 	/*
18335485Seh146360 	 * notify to update the link, and WPA
18343847Seh146360 	 */
18353847Seh146360 	if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) {
18365485Seh146360 		ieee80211_notify_node_join(ic, ic->ic_bss);
18373847Seh146360 	} else if ((ic->ic_state == IEEE80211_S_RUN) &&
18384812Seh146360 	    (state != IEEE80211_S_RUN)) {
18395485Seh146360 		ieee80211_notify_node_leave(ic, ic->ic_bss);
18403847Seh146360 	}
18413847Seh146360 
18423847Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
18433847Seh146360 	    "ipw2200_newstat(): %s -> %s\n",
18443847Seh146360 	    ieee80211_state_name[ic->ic_state],
18453847Seh146360 	    ieee80211_state_name[state]));
18463847Seh146360 
18473847Seh146360 	ic->ic_state = state;
18483847Seh146360 	return (DDI_SUCCESS);
18493847Seh146360 }
18503847Seh146360 /*
18513847Seh146360  * GLD operations
18523847Seh146360  */
18533847Seh146360 /* ARGSUSED */
18543847Seh146360 static int
ipw2200_m_stat(void * arg,uint_t stat,uint64_t * val)18553847Seh146360 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val)
18563847Seh146360 {
18577194Seh146360 	ieee80211com_t		*ic = (ieee80211com_t *)arg;
18587194Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
18593847Seh146360 
18603847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
18613847Seh146360 	    CE_CONT,
18623847Seh146360 	    "ipw2200_m_stat(): enter\n"));
18633847Seh146360 	/*
18643847Seh146360 	 * Some of below statistic data are from hardware, some from net80211
18653847Seh146360 	 */
18663847Seh146360 	switch (stat) {
18677194Seh146360 	case MAC_STAT_NOXMTBUF:
18687194Seh146360 		*val = ic->ic_stats.is_tx_nobuf;
18697194Seh146360 		break;
18707194Seh146360 	case MAC_STAT_IERRORS:
18717194Seh146360 		*val = sc->sc_stats.sc_rx_len_err;
18727194Seh146360 		break;
18737194Seh146360 	case MAC_STAT_OERRORS:
18747194Seh146360 		*val = sc->sc_stats.sc_tx_discard +
18757194Seh146360 		    sc->sc_stats.sc_tx_alloc_fail +
18767194Seh146360 		    sc->sc_stats.sc_tx_encap_fail +
18777194Seh146360 		    sc->sc_stats.sc_tx_crypto_fail;
18787194Seh146360 		break;
18793847Seh146360 	case MAC_STAT_RBYTES:
18803847Seh146360 		*val = ic->ic_stats.is_rx_bytes;
18813847Seh146360 		break;
18823847Seh146360 	case MAC_STAT_IPACKETS:
18833847Seh146360 		*val = ic->ic_stats.is_rx_frags;
18843847Seh146360 		break;
18853847Seh146360 	case MAC_STAT_OBYTES:
18863847Seh146360 		*val = ic->ic_stats.is_tx_bytes;
18873847Seh146360 		break;
18883847Seh146360 	case MAC_STAT_OPACKETS:
18893847Seh146360 		*val = ic->ic_stats.is_tx_frags;
18903847Seh146360 		break;
18913847Seh146360 	/*
18923847Seh146360 	 * Get below from hardware statistic, retraive net80211 value once 1s
18933847Seh146360 	 */
18943847Seh146360 	case WIFI_STAT_TX_FRAGS:
18953847Seh146360 	case WIFI_STAT_MCAST_TX:
18963847Seh146360 	case WIFI_STAT_TX_FAILED:
18973847Seh146360 	case WIFI_STAT_TX_RETRANS:
18983847Seh146360 	/*
18993847Seh146360 	 * Get blow information from net80211
19003847Seh146360 	 */
19013847Seh146360 	case WIFI_STAT_RTS_SUCCESS:
19023847Seh146360 	case WIFI_STAT_RTS_FAILURE:
19033847Seh146360 	case WIFI_STAT_ACK_FAILURE:
19043847Seh146360 	case WIFI_STAT_RX_FRAGS:
19053847Seh146360 	case WIFI_STAT_MCAST_RX:
19063847Seh146360 	case WIFI_STAT_RX_DUPS:
19073847Seh146360 	case WIFI_STAT_FCS_ERRORS:
19083847Seh146360 	case WIFI_STAT_WEP_ERRORS:
19093847Seh146360 		return (ieee80211_stat(ic, stat, val));
19103847Seh146360 	/*
19113847Seh146360 	 * Need be supported later
19123847Seh146360 	 */
19133847Seh146360 	case MAC_STAT_IFSPEED:
19143847Seh146360 	default:
19153847Seh146360 		return (ENOTSUP);
19163847Seh146360 	}
19173847Seh146360 	return (0);
19183847Seh146360 }
19193847Seh146360 
19203847Seh146360 /* ARGSUSED */
19213847Seh146360 static int
ipw2200_m_multicst(void * arg,boolean_t add,const uint8_t * mca)19223847Seh146360 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
19233847Seh146360 {
19243847Seh146360 	/* not supported */
19253847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
19263847Seh146360 	    CE_CONT,
19273847Seh146360 	    "ipw2200_m_multicst(): enter\n"));
19283847Seh146360 
19297194Seh146360 	return (0);
19303847Seh146360 }
19313847Seh146360 
19323847Seh146360 /*
19333847Seh146360  * Multithread handler for linkstatus, fatal error recovery, get statistic
19343847Seh146360  */
19353847Seh146360 static void
ipw2200_thread(struct ipw2200_softc * sc)19363847Seh146360 ipw2200_thread(struct ipw2200_softc *sc)
19373847Seh146360 {
19383847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
19394298Seh146360 	enum ieee80211_state	ostate;
19403847Seh146360 	int32_t			nlstate;
19413847Seh146360 	int			stat_cnt = 0;
19423847Seh146360 
19433847Seh146360 	IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
19443847Seh146360 	    "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate));
19453847Seh146360 
19463847Seh146360 	mutex_enter(&sc->sc_mflock);
19473847Seh146360 
19483847Seh146360 	while (sc->sc_mfthread_switch) {
19493847Seh146360 		/*
19507194Seh146360 		 * when radio is off or SUSPEND status, nothing to do
19517194Seh146360 		 */
19527194Seh146360 		if ((ipw2200_radio_status(sc) == 0) ||
19537194Seh146360 		    sc->sc_flags & IPW2200_FLAG_SUSPEND) {
19547194Seh146360 			goto wait_loop;
19557194Seh146360 		}
19567194Seh146360 
19577194Seh146360 		/*
19583847Seh146360 		 * notify the link state
19593847Seh146360 		 */
19603847Seh146360 		if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) {
19613847Seh146360 
19623847Seh146360 			IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
19633847Seh146360 			    "ipw2200_thread(): link status --> %d\n",
19643847Seh146360 			    sc->sc_linkstate));
19653847Seh146360 
19663847Seh146360 			sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE;
19673847Seh146360 			nlstate = sc->sc_linkstate;
19683847Seh146360 
19693847Seh146360 			mutex_exit(&sc->sc_mflock);
19703847Seh146360 			mac_link_update(ic->ic_mach, nlstate);
19713847Seh146360 			mutex_enter(&sc->sc_mflock);
19723847Seh146360 		}
19733847Seh146360 
19743847Seh146360 		/*
19753847Seh146360 		 * recovery fatal error
19763847Seh146360 		 */
19773847Seh146360 		if (ic->ic_mach &&
19783847Seh146360 		    (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) {
19793847Seh146360 
19803847Seh146360 			IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
19813847Seh146360 			    "ipw2200_thread(): "
19823847Seh146360 			    "try to recover fatal hw error\n"));
19833847Seh146360 
19843847Seh146360 			sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER;
19853847Seh146360 			mutex_exit(&sc->sc_mflock);
19864298Seh146360 
19875485Seh146360 			/* stop again */
19884298Seh146360 			ostate = ic->ic_state;
19893847Seh146360 			(void) ipw2200_init(sc); /* Force state machine */
19905485Seh146360 
19913847Seh146360 			/*
19923847Seh146360 			 * workround. Delay for a while after init especially
19933847Seh146360 			 * when something wrong happened already.
19943847Seh146360 			 */
19953847Seh146360 			delay(drv_usectohz(delay_fatal_recover));
19964298Seh146360 
19974298Seh146360 			/*
19984298Seh146360 			 * Init scan will recovery the original connection if
19994298Seh146360 			 * the original state is run
20004298Seh146360 			 */
20014298Seh146360 			if (ostate != IEEE80211_S_INIT)
20024298Seh146360 				ieee80211_begin_scan(ic, 0);
20034298Seh146360 
20043847Seh146360 			mutex_enter(&sc->sc_mflock);
20053847Seh146360 		}
20063847Seh146360 
20073847Seh146360 		/*
20083847Seh146360 		 * get statistic, the value will be retrieved by m_stat
20093847Seh146360 		 */
20103847Seh146360 		if (stat_cnt == 10) {
20113847Seh146360 
20123847Seh146360 			stat_cnt = 0; /* re-start */
20133847Seh146360 			mutex_exit(&sc->sc_mflock);
20143847Seh146360 			ipw2200_get_statistics(sc);
20153847Seh146360 			mutex_enter(&sc->sc_mflock);
20163847Seh146360 
20173847Seh146360 		} else
20183847Seh146360 			stat_cnt++; /* until 1s */
20193847Seh146360 
20207194Seh146360 wait_loop:
20213847Seh146360 		mutex_exit(&sc->sc_mflock);
20223847Seh146360 		delay(drv_usectohz(delay_aux_thread));
20233847Seh146360 		mutex_enter(&sc->sc_mflock);
20243847Seh146360 
20253847Seh146360 	}
20263847Seh146360 	sc->sc_mf_thread = NULL;
20273847Seh146360 	cv_signal(&sc->sc_mfthread_cv);
20283847Seh146360 	mutex_exit(&sc->sc_mflock);
20293847Seh146360 }
20303847Seh146360 
20313847Seh146360 static int
ipw2200_m_start(void * arg)20323847Seh146360 ipw2200_m_start(void *arg)
20333847Seh146360 {
20343847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
20354298Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
20363847Seh146360 
20373847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
20383847Seh146360 	    "ipw2200_m_start(): enter\n"));
20393847Seh146360 	/*
20403847Seh146360 	 * initialize ipw2200 hardware, everything ok will start scan
20413847Seh146360 	 */
20423847Seh146360 	(void) ipw2200_init(sc);
20435485Seh146360 
20444298Seh146360 	/*
20454298Seh146360 	 * set the state machine to INIT
20464298Seh146360 	 */
20474298Seh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
20483847Seh146360 
20493847Seh146360 	sc->sc_flags |= IPW2200_FLAG_RUNNING;
20503847Seh146360 
20515485Seh146360 	/*
20525485Seh146360 	 * fix KCF bug. - workaround, need to fix it in net80211
20535485Seh146360 	 */
20545485Seh146360 	(void) crypto_mech2id(SUN_CKM_RC4);
20555485Seh146360 
20567194Seh146360 	return (0);
20573847Seh146360 }
20583847Seh146360 
20593847Seh146360 static void
ipw2200_m_stop(void * arg)20603847Seh146360 ipw2200_m_stop(void *arg)
20613847Seh146360 {
20623847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
20634298Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
20643847Seh146360 
20653847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
20663847Seh146360 	    "ipw2200_m_stop(): enter\n"));
20673847Seh146360 
20683847Seh146360 	ipw2200_stop(sc);
20694298Seh146360 	/*
20704298Seh146360 	 * set the state machine to INIT
20714298Seh146360 	 */
20724298Seh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
20733847Seh146360 
20743847Seh146360 	sc->sc_flags &= ~IPW2200_FLAG_RUNNING;
20753847Seh146360 }
20763847Seh146360 
20773847Seh146360 static int
ipw2200_m_unicst(void * arg,const uint8_t * macaddr)20783847Seh146360 ipw2200_m_unicst(void *arg, const uint8_t *macaddr)
20793847Seh146360 {
20803847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
20813847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
20823847Seh146360 	int			err;
20833847Seh146360 
20843847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
20853847Seh146360 	    "ipw2200_m_unicst(): enter\n"));
20863847Seh146360 
20873847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
20883847Seh146360 	    "ipw2200_m_unicst(): GLD setting MAC address to "
20893847Seh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
20903847Seh146360 	    macaddr[0], macaddr[1], macaddr[2],
20913847Seh146360 	    macaddr[3], macaddr[4], macaddr[5]));
20923847Seh146360 
20933847Seh146360 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
20943847Seh146360 
20953847Seh146360 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
20963847Seh146360 
20973847Seh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
20983847Seh146360 			err = ipw2200_config(sc);
20993847Seh146360 			if (err != DDI_SUCCESS) {
21003847Seh146360 				IPW2200_WARN((sc->sc_dip, CE_WARN,
21013847Seh146360 				    "ipw2200_m_unicst(): "
21023847Seh146360 				    "device configuration failed\n"));
21033847Seh146360 				goto fail;
21043847Seh146360 			}
21053847Seh146360 		}
21063847Seh146360 	}
21077194Seh146360 	return (0);
21083847Seh146360 fail:
21097194Seh146360 	return (EIO);
21103847Seh146360 }
21113847Seh146360 
21123847Seh146360 static int
ipw2200_m_promisc(void * arg,boolean_t on)21133847Seh146360 ipw2200_m_promisc(void *arg, boolean_t on)
21143847Seh146360 {
21153847Seh146360 	/* not supported */
21163847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
21173847Seh146360 
21183847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
21193847Seh146360 	    "ipw2200_m_promisc(): enter. "
21203847Seh146360 	    "GLD setting promiscuous mode - %d\n", on));
21213847Seh146360 
21227194Seh146360 	return (0);
21233847Seh146360 }
21243847Seh146360 
21253847Seh146360 static mblk_t *
ipw2200_m_tx(void * arg,mblk_t * mp)21263847Seh146360 ipw2200_m_tx(void *arg, mblk_t *mp)
21273847Seh146360 {
21283847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
21293847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
21303847Seh146360 	mblk_t			*next;
21313847Seh146360 
21323847Seh146360 	/*
21337194Seh146360 	 * when driver in on suspend state, freemsgchain directly
21347194Seh146360 	 */
21357194Seh146360 	if (sc->sc_flags & IPW2200_FLAG_SUSPEND) {
21367194Seh146360 		IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
21377194Seh146360 		    "ipw2200_m_tx(): suspend status, discard msg\n"));
21387194Seh146360 		sc->sc_stats.sc_tx_discard++; /* discard data */
21397194Seh146360 		freemsgchain(mp);
21407194Seh146360 		return (NULL);
21417194Seh146360 	}
21427194Seh146360 
21437194Seh146360 	/*
21443847Seh146360 	 * No data frames go out unless we're associated; this
21453847Seh146360 	 * should not happen as the 802.11 layer does not enable
21463847Seh146360 	 * the xmit queue until we enter the RUN state.
21473847Seh146360 	 */
21483847Seh146360 	if (ic->ic_state != IEEE80211_S_RUN) {
21493847Seh146360 		IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
21503847Seh146360 		    "ipw2200_m_tx(): discard msg, ic_state = %u\n",
21513847Seh146360 		    ic->ic_state));
21527194Seh146360 		sc->sc_stats.sc_tx_discard++; /* discard data */
21533847Seh146360 		freemsgchain(mp);
21543847Seh146360 		return (NULL);
21553847Seh146360 	}
21563847Seh146360 
21573847Seh146360 	while (mp != NULL) {
21583847Seh146360 		next = mp->b_next;
21593847Seh146360 		mp->b_next = NULL;
21603847Seh146360 		if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) ==
21615485Seh146360 		    ENOMEM) {
21623847Seh146360 			mp->b_next = next;
21633847Seh146360 			break;
21643847Seh146360 		}
21653847Seh146360 		mp = next;
21663847Seh146360 	}
21673847Seh146360 	return (mp);
21683847Seh146360 }
21693847Seh146360 
21705485Seh146360 /*
21715485Seh146360  * ipw2200_send(): send data. softway to handle crypto_encap.
21725485Seh146360  */
21733847Seh146360 static int
ipw2200_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)21743847Seh146360 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
21753847Seh146360 {
21763847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
21773847Seh146360 	struct ieee80211_node	*in;
21783847Seh146360 	struct ieee80211_frame	*wh;
21795485Seh146360 	struct ieee80211_key	*k;
21805485Seh146360 	mblk_t			*m0, *m;
21813847Seh146360 	size_t			cnt, off;
21823847Seh146360 	struct ipw2200_tx_desc	*txdsc;
21833847Seh146360 	struct dma_region	*dr;
21843847Seh146360 	uint32_t		idx;
21855485Seh146360 	int			err = DDI_SUCCESS;
21863847Seh146360 	/* tmp pointer, used to pack header and payload */
21873847Seh146360 	uint8_t			*p;
21883847Seh146360 
21893847Seh146360 	ASSERT(mp->b_next == NULL);
21903847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
21913847Seh146360 	    "ipw2200_send(): enter\n"));
21923847Seh146360 
21933847Seh146360 	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) {
21943847Seh146360 		/*
21953847Seh146360 		 * skip all management frames since ipw2200 won't generate any
21963847Seh146360 		 * management frames. Therefore, drop this package.
21973847Seh146360 		 */
21983847Seh146360 		freemsg(mp);
21995485Seh146360 		err = DDI_FAILURE;
22003847Seh146360 		goto fail0;
22013847Seh146360 	}
22023847Seh146360 
22033847Seh146360 	mutex_enter(&sc->sc_tx_lock);
22047194Seh146360 	if (sc->sc_flags & IPW2200_FLAG_SUSPEND) {
22057194Seh146360 		/*
22067194Seh146360 		 * when sending data, system runs into suspend status,
22077194Seh146360 		 * return fail directly
22087194Seh146360 		 */
22097194Seh146360 		err = ENXIO;
22107194Seh146360 		goto fail0;
22117194Seh146360 	}
22123847Seh146360 
22133847Seh146360 	/*
22143847Seh146360 	 * need 1 empty descriptor
22153847Seh146360 	 */
22163847Seh146360 	if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) {
22175485Seh146360 		mutex_enter(&sc->sc_resched_lock);
22183847Seh146360 		IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN,
22193847Seh146360 		    "ipw2200_send(): no enough descriptors(%d)\n",
22203847Seh146360 		    sc->sc_tx_free));
22213847Seh146360 		ic->ic_stats.is_tx_nobuf++; /* no enough buffer */
22223847Seh146360 		sc->sc_flags |= IPW2200_FLAG_TX_SCHED;
22235485Seh146360 		err = ENOMEM;
22245485Seh146360 		mutex_exit(&sc->sc_resched_lock);
22253847Seh146360 		goto fail1;
22263847Seh146360 	}
22273847Seh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
22283847Seh146360 	    "ipw2200_send():  tx-free=%d,tx-curr=%d\n",
22293847Seh146360 	    sc->sc_tx_free, sc->sc_tx_cur));
22303847Seh146360 
22315485Seh146360 	/*
22325485Seh146360 	 * put the mp into one blk, and use it to do the crypto_encap
22335485Seh146360 	 * if necessaary.
22345485Seh146360 	 */
22355485Seh146360 	m = allocb(msgdsize(mp) + 32, BPRI_MED);
22365485Seh146360 	if (m == NULL) { /* can not alloc buf, drop this package */
22375485Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
22385485Seh146360 		    "ipw2200_send(): msg allocation failed\n"));
22395485Seh146360 		freemsg(mp);
22407194Seh146360 		sc->sc_stats.sc_tx_alloc_fail++; /* alloc fail */
22417194Seh146360 		ic->ic_stats.is_tx_failed++;  /* trans failed */
22425485Seh146360 		err = DDI_FAILURE;
22435485Seh146360 		goto fail1;
22445485Seh146360 	}
22455485Seh146360 	for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
22465485Seh146360 		cnt = MBLKL(m0);
22475485Seh146360 		(void) memcpy(m->b_rptr + off, m0->b_rptr, cnt);
22485485Seh146360 		off += cnt;
22495485Seh146360 	}
22505485Seh146360 	m->b_wptr += off;
22515485Seh146360 
22525485Seh146360 	/*
22535485Seh146360 	 * find tx_node, and encapsulate the data
22545485Seh146360 	 */
22555485Seh146360 	wh = (struct ieee80211_frame *)m->b_rptr;
22563847Seh146360 	in = ieee80211_find_txnode(ic, wh->i_addr1);
22573847Seh146360 	if (in == NULL) { /* can not find the tx node, drop the package */
22587194Seh146360 		sc->sc_stats.sc_tx_encap_fail++; /* tx encap fail */
22597194Seh146360 		ic->ic_stats.is_tx_failed++; /* trans failed */
22603847Seh146360 		freemsg(mp);
22615485Seh146360 		err = DDI_FAILURE;
22625485Seh146360 		goto fail2;
22633847Seh146360 	}
22643847Seh146360 	in->in_inact = 0;
22655485Seh146360 
22665485Seh146360 	(void) ieee80211_encap(ic, m, in);
22673847Seh146360 	ieee80211_free_node(in);
22683847Seh146360 
22695485Seh146360 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
22705485Seh146360 		k = ieee80211_crypto_encap(ic, m);
22715485Seh146360 		if (k == NULL) { /* can not get the key, drop packages */
22725485Seh146360 			IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
22735485Seh146360 			    "ipw2200_send(): "
22745485Seh146360 			    "Encrypting 802.11 frame failed\n"));
22757194Seh146360 			sc->sc_stats.sc_tx_crypto_fail++; /* tx encap fail */
22767194Seh146360 			ic->ic_stats.is_tx_failed++; /* trans failed */
22775485Seh146360 			freemsg(mp);
22785485Seh146360 			err = DDI_FAILURE;
22795485Seh146360 			goto fail2;
22805485Seh146360 		}
22815485Seh146360 		wh = (struct ieee80211_frame *)m->b_rptr;
22825485Seh146360 	}
22835485Seh146360 
22843847Seh146360 	/*
22855485Seh146360 	 * get txdsc
22863847Seh146360 	 */
22873847Seh146360 	idx	= sc->sc_tx_cur;
22883847Seh146360 	txdsc	= &sc->sc_txdsc[idx];
22893847Seh146360 	(void) memset(txdsc, 0, sizeof (*txdsc));
22903847Seh146360 	/*
22913847Seh146360 	 * extract header from message
22923847Seh146360 	 */
22933847Seh146360 	p	= (uint8_t *)&txdsc->wh;
22945485Seh146360 	off	= sizeof (struct ieee80211_frame);
22955485Seh146360 	(void) memcpy(p, m->b_rptr, off);
22963847Seh146360 	/*
22973847Seh146360 	 * extract payload from message
22983847Seh146360 	 */
22993847Seh146360 	dr	= &sc->sc_dma_txbufs[idx];
23003847Seh146360 	p	= sc->sc_txbufs[idx];
23015485Seh146360 	cnt	= MBLKL(m);
23025485Seh146360 	(void) memcpy(p, m->b_rptr + off, cnt - off);
23035485Seh146360 	cnt    -= off;
23043847Seh146360 
23053847Seh146360 	txdsc->hdr.type   = IPW2200_HDR_TYPE_DATA;
23063847Seh146360 	txdsc->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
23073847Seh146360 	txdsc->cmd	  = IPW2200_DATA_CMD_TX;
23085485Seh146360 	txdsc->len	  = LE_16(cnt);
23093847Seh146360 	txdsc->flags	  = 0;
23103847Seh146360 
23113847Seh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
23123847Seh146360 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
23133847Seh146360 			txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
23143847Seh146360 	} else if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
23153847Seh146360 		txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
23163847Seh146360 
23175485Seh146360 	/* always set it to none wep, because it's handled by software */
23185485Seh146360 	txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP;
23193847Seh146360 
23203847Seh146360 	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
23213847Seh146360 		txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE;
23223847Seh146360 
23233847Seh146360 	txdsc->nseg	    = LE_32(1);
23243847Seh146360 	txdsc->seg_addr[0]  = LE_32(dr->dr_pbase);
23255485Seh146360 	txdsc->seg_len[0]   = LE_32(cnt);
23263847Seh146360 
23273847Seh146360 	/*
23283847Seh146360 	 * DMA sync: buffer and desc
23293847Seh146360 	 */
23303847Seh146360 	(void) ddi_dma_sync(dr->dr_hnd, 0,
23313847Seh146360 	    IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV);
23323847Seh146360 	(void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd,
23333847Seh146360 	    idx * sizeof (struct ipw2200_tx_desc),
23343847Seh146360 	    sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV);
23353847Seh146360 
23363847Seh146360 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE);
23373847Seh146360 	sc->sc_tx_free--;
23383847Seh146360 
23393847Seh146360 	/*
23403847Seh146360 	 * update txcur
23413847Seh146360 	 */
23423847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
23433847Seh146360 
23443847Seh146360 	/*
23453847Seh146360 	 * success, free the original message
23463847Seh146360 	 */
23473847Seh146360 	if (mp)
23483847Seh146360 		freemsg(mp);
23495485Seh146360 fail2:
23505485Seh146360 	if (m)
23515485Seh146360 		freemsg(m);
23523847Seh146360 fail1:
23533847Seh146360 	mutex_exit(&sc->sc_tx_lock);
23543847Seh146360 fail0:
23553847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
23563847Seh146360 	    "ipw2200_send(): exit - err=%d\n", err));
23573847Seh146360 
23583847Seh146360 	return (err);
23593847Seh146360 }
23603847Seh146360 
23613847Seh146360 /*
23623847Seh146360  * IOCTL handlers
23633847Seh146360  */
23643847Seh146360 #define	IEEE80211_IOCTL_REQUIRED	(1)
23653847Seh146360 #define	IEEE80211_IOCTL_NOT_REQUIRED	(0)
23663847Seh146360 static void
ipw2200_m_ioctl(void * arg,queue_t * q,mblk_t * m)23673847Seh146360 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m)
23683847Seh146360 {
23693847Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
23703847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
23713847Seh146360 	uint32_t		err;
23723847Seh146360 
23733847Seh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
23743847Seh146360 	    "ipw2200_m_ioctl(): enter\n"));
23753847Seh146360 
23763847Seh146360 	/*
23773847Seh146360 	 * Check whether or not need to handle this in net80211
23783847Seh146360 	 *
23793847Seh146360 	 */
23803847Seh146360 	if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED)
23813847Seh146360 		return;
23823847Seh146360 
23833847Seh146360 	err = ieee80211_ioctl(ic, q, m);
23843847Seh146360 	if (err == ENETRESET) {
23853847Seh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
23863847Seh146360 			(void) ipw2200_m_start(sc);
23873847Seh146360 			(void) ieee80211_new_state(ic,
23883847Seh146360 			    IEEE80211_S_SCAN, -1);
23893847Seh146360 		}
23903847Seh146360 	}
23913847Seh146360 	if (err == ERESTART) {
23923847Seh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING)
23933847Seh146360 			(void) ipw2200_chip_reset(sc);
23943847Seh146360 	}
23953847Seh146360 }
23963847Seh146360 static int
ipw2200_ioctl(struct ipw2200_softc * sc,queue_t * q,mblk_t * m)23973847Seh146360 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m)
23983847Seh146360 {
23993847Seh146360 	struct iocblk	*iocp;
24007194Seh146360 	uint32_t	len, ret, cmd, mblen;
24013847Seh146360 	mblk_t		*m0;
24023847Seh146360 	boolean_t	need_privilege;
24033847Seh146360 	boolean_t	need_net80211;
24043847Seh146360 
24057194Seh146360 	mblen = MBLKL(m);
24067194Seh146360 	if (mblen < sizeof (struct iocblk)) {
24073847Seh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
24083847Seh146360 		    "ipw2200_ioctl(): ioctl buffer too short, %u\n",
24097194Seh146360 		    mblen));
24103847Seh146360 		miocnak(q, m, 0, EINVAL);
24113847Seh146360 		/*
24123847Seh146360 		 * Buf not enough, do not need net80211 either
24133847Seh146360 		 */
24143847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
24153847Seh146360 	}
24163847Seh146360 
24173847Seh146360 	/*
24183847Seh146360 	 * Validate the command
24193847Seh146360 	 */
24207194Seh146360 	iocp = (struct iocblk *)(uintptr_t)m->b_rptr;
24213847Seh146360 	iocp->ioc_error = 0;
24223847Seh146360 	cmd = iocp->ioc_cmd;
24233847Seh146360 	need_privilege = B_TRUE;
24243847Seh146360 	switch (cmd) {
24253847Seh146360 	case WLAN_SET_PARAM:
24263847Seh146360 	case WLAN_COMMAND:
24273847Seh146360 		break;
24283847Seh146360 	case WLAN_GET_PARAM:
24293847Seh146360 		need_privilege = B_FALSE;
24303847Seh146360 		break;
24313847Seh146360 	default:
24323847Seh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
24333847Seh146360 		    "ipw2200_ioctl(): unknown cmd 0x%x", cmd));
24343847Seh146360 		miocnak(q, m, 0, EINVAL);
24353847Seh146360 		/*
24363847Seh146360 		 * Unknown cmd, do not need net80211 either
24373847Seh146360 		 */
24383847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
24393847Seh146360 	}
24403847Seh146360 
24417408SSebastien.Roy@Sun.COM 	if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) {
24427408SSebastien.Roy@Sun.COM 		miocnak(q, m, 0, ret);
24433847Seh146360 		/*
24447408SSebastien.Roy@Sun.COM 		 * privilege check fail, do not need net80211 either
24453847Seh146360 		 */
24467408SSebastien.Roy@Sun.COM 		return (IEEE80211_IOCTL_NOT_REQUIRED);
24473847Seh146360 	}
24487408SSebastien.Roy@Sun.COM 
24493847Seh146360 	/*
24503847Seh146360 	 * sanity check
24513847Seh146360 	 */
24523847Seh146360 	m0 = m->b_cont;
24533847Seh146360 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
24543847Seh146360 	    m0 == NULL) {
24553847Seh146360 		miocnak(q, m, 0, EINVAL);
24563847Seh146360 		/*
24573847Seh146360 		 * invalid format, do not need net80211 either
24583847Seh146360 		 */
24593847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
24603847Seh146360 	}
24613847Seh146360 	/*
24623847Seh146360 	 * assuming single data block
24633847Seh146360 	 */
24643847Seh146360 	if (m0->b_cont) {
24653847Seh146360 		freemsg(m0->b_cont);
24663847Seh146360 		m0->b_cont = NULL;
24673847Seh146360 	}
24683847Seh146360 
24693847Seh146360 	need_net80211 = B_FALSE;
24703847Seh146360 	ret = ipw2200_getset(sc, m0, cmd, &need_net80211);
24713847Seh146360 	if (!need_net80211) {
24723847Seh146360 		len = msgdsize(m0);
24733847Seh146360 
24743847Seh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
24753847Seh146360 		    "ipw2200_ioctl(): go to call miocack with "
24763847Seh146360 		    "ret = %d, len = %d\n", ret, len));
24773847Seh146360 		miocack(q, m, len, ret);
24783847Seh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
24793847Seh146360 	}
24803847Seh146360 
24813847Seh146360 	/*
24823847Seh146360 	 * IEEE80211_IOCTL - need net80211 handle
24833847Seh146360 	 */
24843847Seh146360 	return (IEEE80211_IOCTL_REQUIRED);
24853847Seh146360 }
24863847Seh146360 
24873847Seh146360 static int
ipw2200_getset(struct ipw2200_softc * sc,mblk_t * m,uint32_t cmd,boolean_t * need_net80211)24883847Seh146360 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd,
24893847Seh146360 	boolean_t *need_net80211)
24903847Seh146360 {
24913847Seh146360 	wldp_t		*infp, *outfp;
24923847Seh146360 	uint32_t	id;
24933847Seh146360 	int		ret;
24943847Seh146360 
24957194Seh146360 	infp  = (wldp_t *)(uintptr_t)m->b_rptr;
24967194Seh146360 	outfp = (wldp_t *)(uintptr_t)m->b_rptr;
24973847Seh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
24983847Seh146360 
24993847Seh146360 	id = infp->wldp_id;
25003847Seh146360 	IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
25013847Seh146360 	    "ipw2200_getset(): id = 0x%x\n", id));
25023847Seh146360 	switch (id) {
25033847Seh146360 	case WL_RADIO: /* which is not supported by net80211 */
25043847Seh146360 		ret = iwi_wificfg_radio(sc, cmd, outfp);
25053847Seh146360 		break;
25063847Seh146360 	case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */
25073847Seh146360 		ret = iwi_wificfg_desrates(outfp);
25083847Seh146360 		break;
25093847Seh146360 	default:
25103847Seh146360 		/*
25113847Seh146360 		 * The wifi IOCTL net80211 supported:
25123847Seh146360 		 *	case WL_ESSID:
25133847Seh146360 		 *	case WL_BSSID:
25143847Seh146360 		 *	case WL_WEP_KEY_TAB:
25153847Seh146360 		 *	case WL_WEP_KEY_ID:
25163847Seh146360 		 *	case WL_AUTH_MODE:
25173847Seh146360 		 *	case WL_ENCRYPTION:
25183847Seh146360 		 *	case WL_BSS_TYPE:
25193847Seh146360 		 *	case WL_ESS_LIST:
25203847Seh146360 		 *	case WL_LINKSTATUS:
25213847Seh146360 		 *	case WL_RSSI:
25223847Seh146360 		 *	case WL_SCAN:
25233847Seh146360 		 *	case WL_LOAD_DEFAULTS:
25243847Seh146360 		 *	case WL_DISASSOCIATE:
25253847Seh146360 		 */
25265838Seh146360 
25275838Seh146360 		/*
25285838Seh146360 		 * When radio is off, need to ignore all ioctl.  What need to
25295838Seh146360 		 * do is to check radio status firstly.  If radio is ON, pass
25305838Seh146360 		 * it to net80211, otherwise, return to upper layer directly.
25315838Seh146360 		 *
25325838Seh146360 		 * Considering the WL_SUCCESS also means WL_CONNECTED for
25335838Seh146360 		 * checking linkstatus, one exception for WL_LINKSTATUS is to
25345838Seh146360 		 * let net80211 handle it.
25355838Seh146360 		 */
25365838Seh146360 		if ((ipw2200_radio_status(sc) == 0) &&
25375838Seh146360 		    (id != WL_LINKSTATUS)) {
25385838Seh146360 
25395838Seh146360 			IPW2200_REPORT((sc->sc_dip, CE_CONT,
25405838Seh146360 			    "iwi: radio is OFF\n"));
25415838Seh146360 
25425838Seh146360 			outfp->wldp_length = WIFI_BUF_OFFSET;
25435838Seh146360 			outfp->wldp_result = WL_SUCCESS;
25445838Seh146360 			ret = 0;
25455838Seh146360 			break;
25465838Seh146360 		}
25475838Seh146360 
25483847Seh146360 		*need_net80211 = B_TRUE; /* let net80211 do the rest */
25493847Seh146360 		return (0);
25503847Seh146360 	}
25513847Seh146360 	/*
25523847Seh146360 	 * we will overwrite everything
25533847Seh146360 	 */
25543847Seh146360 	m->b_wptr = m->b_rptr + outfp->wldp_length;
25553847Seh146360 	return (ret);
25563847Seh146360 }
25573847Seh146360 
25588367SFei.Feng@Sun.COM /*
25598367SFei.Feng@Sun.COM  * Call back functions for get/set proporty
25608367SFei.Feng@Sun.COM  */
25618367SFei.Feng@Sun.COM static int
ipw2200_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)25628367SFei.Feng@Sun.COM ipw2200_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2563*11878SVenu.Iyer@Sun.COM     uint_t wldp_length, void *wldp_buf)
25648367SFei.Feng@Sun.COM {
25658367SFei.Feng@Sun.COM 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
25668367SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
25678367SFei.Feng@Sun.COM 	int			err = 0;
25688367SFei.Feng@Sun.COM 
25698367SFei.Feng@Sun.COM 	switch (wldp_pr_num) {
25708367SFei.Feng@Sun.COM 	/* mac_prop_id */
25718367SFei.Feng@Sun.COM 	case MAC_PROP_WL_DESIRED_RATES:
25728367SFei.Feng@Sun.COM 		IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
25738367SFei.Feng@Sun.COM 		    "ipw2200_m_getprop(): Not Support DESIRED_RATES\n"));
25748367SFei.Feng@Sun.COM 		break;
25758367SFei.Feng@Sun.COM 	case MAC_PROP_WL_RADIO:
25768367SFei.Feng@Sun.COM 		*(wl_linkstatus_t *)wldp_buf = ipw2200_radio_status(sc);
25778367SFei.Feng@Sun.COM 		break;
25788367SFei.Feng@Sun.COM 	default:
25798367SFei.Feng@Sun.COM 		/* go through net80211 */
2580*11878SVenu.Iyer@Sun.COM 		err = ieee80211_getprop(ic, pr_name, wldp_pr_num,
2581*11878SVenu.Iyer@Sun.COM 		    wldp_length, wldp_buf);
25828367SFei.Feng@Sun.COM 		break;
25838367SFei.Feng@Sun.COM 	}
25848367SFei.Feng@Sun.COM 
25858367SFei.Feng@Sun.COM 	return (err);
25868367SFei.Feng@Sun.COM }
25878367SFei.Feng@Sun.COM 
2588*11878SVenu.Iyer@Sun.COM static void
ipw2200_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wlpd_pr_num,mac_prop_info_handle_t mph)2589*11878SVenu.Iyer@Sun.COM ipw2200_m_propinfo(void *arg, const char *pr_name,
2590*11878SVenu.Iyer@Sun.COM     mac_prop_id_t wlpd_pr_num, mac_prop_info_handle_t mph)
2591*11878SVenu.Iyer@Sun.COM {
2592*11878SVenu.Iyer@Sun.COM 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2593*11878SVenu.Iyer@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
2594*11878SVenu.Iyer@Sun.COM 
2595*11878SVenu.Iyer@Sun.COM 	ieee80211_propinfo(ic, pr_name, wlpd_pr_num, mph);
2596*11878SVenu.Iyer@Sun.COM }
2597*11878SVenu.Iyer@Sun.COM 
25988367SFei.Feng@Sun.COM static int
ipw2200_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)25998367SFei.Feng@Sun.COM ipw2200_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
26008367SFei.Feng@Sun.COM     uint_t wldp_length, const void *wldp_buf)
26018367SFei.Feng@Sun.COM {
26028367SFei.Feng@Sun.COM 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
26038367SFei.Feng@Sun.COM 	struct ieee80211com	*ic = &sc->sc_ic;
26048367SFei.Feng@Sun.COM 	int			err;
26058367SFei.Feng@Sun.COM 
26068367SFei.Feng@Sun.COM 	switch (wldp_pr_num) {
26078367SFei.Feng@Sun.COM 	/* mac_prop_id */
26088367SFei.Feng@Sun.COM 	case MAC_PROP_WL_DESIRED_RATES:
26098367SFei.Feng@Sun.COM 		IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
26108367SFei.Feng@Sun.COM 		    "ipw2200_m_setprop(): Not Support DESIRED_RATES\n"));
26118367SFei.Feng@Sun.COM 		err = ENOTSUP;
26128367SFei.Feng@Sun.COM 		break;
26138367SFei.Feng@Sun.COM 	case MAC_PROP_WL_RADIO:
26148367SFei.Feng@Sun.COM 		IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
26158367SFei.Feng@Sun.COM 		    "ipw2200_m_setprop(): Not Support RADIO\n"));
26168367SFei.Feng@Sun.COM 		err = ENOTSUP;
26178367SFei.Feng@Sun.COM 		break;
26188367SFei.Feng@Sun.COM 	default:
26198367SFei.Feng@Sun.COM 		/* go through net80211 */
26208367SFei.Feng@Sun.COM 		err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
26218367SFei.Feng@Sun.COM 		    wldp_buf);
26228367SFei.Feng@Sun.COM 		break;
26238367SFei.Feng@Sun.COM 	}
26248367SFei.Feng@Sun.COM 
26258367SFei.Feng@Sun.COM 	if (err == ENETRESET) {
26268367SFei.Feng@Sun.COM 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
26278367SFei.Feng@Sun.COM 			(void) ipw2200_m_start(sc);
26288367SFei.Feng@Sun.COM 			(void) ieee80211_new_state(ic,
26298367SFei.Feng@Sun.COM 			    IEEE80211_S_SCAN, -1);
26308367SFei.Feng@Sun.COM 		}
26318367SFei.Feng@Sun.COM 		err = 0;
26328367SFei.Feng@Sun.COM 	}
26338367SFei.Feng@Sun.COM 
26348367SFei.Feng@Sun.COM 	return (err);
26358367SFei.Feng@Sun.COM }
26368367SFei.Feng@Sun.COM 
26373847Seh146360 static int
iwi_wificfg_radio(struct ipw2200_softc * sc,uint32_t cmd,wldp_t * outfp)26383847Seh146360 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp)
26393847Seh146360 {
26403847Seh146360 	uint32_t	ret = ENOTSUP;
26413847Seh146360 
26423847Seh146360 	switch (cmd) {
26433847Seh146360 	case WLAN_GET_PARAM:
26443847Seh146360 		*(wl_linkstatus_t *)(outfp->wldp_buf) =
26453847Seh146360 		    ipw2200_radio_status(sc);
26463847Seh146360 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t);
26473847Seh146360 		outfp->wldp_result = WL_SUCCESS;
26483847Seh146360 		ret = 0; /* command success */
26493847Seh146360 		break;
26503847Seh146360 	case WLAN_SET_PARAM:
26513847Seh146360 	default:
26523847Seh146360 		break;
26533847Seh146360 	}
26543847Seh146360 	return (ret);
26553847Seh146360 }
26563847Seh146360 
26573847Seh146360 static int
iwi_wificfg_desrates(wldp_t * outfp)26583847Seh146360 iwi_wificfg_desrates(wldp_t *outfp)
26593847Seh146360 {
26603847Seh146360 	/* return success, but with result NOTSUPPORTED */
26613847Seh146360 	outfp->wldp_length = WIFI_BUF_OFFSET;
26623847Seh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
26633847Seh146360 	return (0);
26643847Seh146360 }
26653847Seh146360 /* End of IOCTL Handlers */
26663847Seh146360 
26673847Seh146360 void
ipw2200_fix_channel(struct ieee80211com * ic,mblk_t * m)26683847Seh146360 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m)
26693847Seh146360 {
26703847Seh146360 	struct ieee80211_frame	*wh;
26713847Seh146360 	uint8_t			subtype;
26723847Seh146360 	uint8_t			*frm, *efrm;
26733847Seh146360 
26743847Seh146360 	wh = (struct ieee80211_frame *)m->b_rptr;
26753847Seh146360 
26763847Seh146360 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
26773847Seh146360 		return;
26783847Seh146360 
26793847Seh146360 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
26803847Seh146360 
26813847Seh146360 	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
26823847Seh146360 	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
26833847Seh146360 		return;
26843847Seh146360 
26853847Seh146360 	/*
26863847Seh146360 	 * assume the message contains only 1 block
26873847Seh146360 	 */
26883847Seh146360 	frm   = (uint8_t *)(wh + 1);
26893847Seh146360 	efrm  = (uint8_t *)m->b_wptr;
26903847Seh146360 	frm  += 12;  /* skip tstamp, bintval and capinfo fields */
26913847Seh146360 	while (frm < efrm) {
26923847Seh146360 		if (*frm == IEEE80211_ELEMID_DSPARMS)
26933847Seh146360 #if IEEE80211_CHAN_MAX < 255
26943847Seh146360 		if (frm[2] <= IEEE80211_CHAN_MAX)
26953847Seh146360 #endif
26963847Seh146360 			ic->ic_curchan = &ic->ic_sup_channels[frm[2]];
26973847Seh146360 		frm += frm[1] + 2;
26983847Seh146360 	}
26993847Seh146360 }
27003847Seh146360 
27013847Seh146360 static void
ipw2200_rcv_frame(struct ipw2200_softc * sc,struct ipw2200_frame * frame)27023847Seh146360 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame)
27033847Seh146360 {
27043847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
27053847Seh146360 	uint8_t			*data = (uint8_t *)frame;
27063847Seh146360 	uint32_t		len;
27073847Seh146360 	struct ieee80211_frame	*wh;
27083847Seh146360 	struct ieee80211_node	*in;
27093847Seh146360 	mblk_t			*m;
27103847Seh146360 
27113847Seh146360 	len = LE_16(frame->len);
27123847Seh146360 	if ((len < sizeof (struct ieee80211_frame_min)) ||
27133847Seh146360 	    (len > IPW2200_RXBUF_SIZE)) {
27143847Seh146360 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
27153847Seh146360 		    "ipw2200_rcv_frame(): bad frame length=%u\n",
27163847Seh146360 		    LE_16(frame->len)));
27177194Seh146360 		sc->sc_stats.sc_rx_len_err++; /* length doesn't work */
27183847Seh146360 		return;
27193847Seh146360 	}
27203847Seh146360 	IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
27213847Seh146360 	    "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len));
27223847Seh146360 
27235485Seh146360 	/*
27245485Seh146360 	 * Skip the frame header, get the real data from the input
27255485Seh146360 	 */
27265485Seh146360 	data += sizeof (struct ipw2200_frame);
27275485Seh146360 
27283847Seh146360 	m = allocb(len, BPRI_MED);
27293847Seh146360 	if (m) {
27303847Seh146360 		(void) memcpy(m->b_wptr, data, len);
27313847Seh146360 		m->b_wptr += len;
27323847Seh146360 
27333847Seh146360 		if (ic->ic_state == IEEE80211_S_SCAN) {
27343847Seh146360 			ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan];
27353847Seh146360 			ipw2200_fix_channel(ic, m);
27363847Seh146360 		}
27375485Seh146360 		wh = (struct ieee80211_frame *)m->b_rptr;
27383847Seh146360 
27393847Seh146360 		in = ieee80211_find_rxnode(ic, wh);
27405485Seh146360 
27413847Seh146360 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
27423847Seh146360 		    "ipw2200_rcv_frame(): "
27435485Seh146360 		    "type = %x, subtype = %x, i_fc[1] = %x, "
27443847Seh146360 		    "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n",
27455485Seh146360 		    wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK,
27465485Seh146360 		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK,
27475485Seh146360 		    wh->i_fc[1] & IEEE80211_FC1_WEP,
27483847Seh146360 		    in->in_esslen,
27493847Seh146360 		    in->in_essid[0], in->in_essid[1], in->in_essid[2],
27503847Seh146360 		    in->in_essid[3], in->in_essid[4], in->in_essid[5]));
27513847Seh146360 
27523847Seh146360 		(void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0);
27533847Seh146360 
27543847Seh146360 		ieee80211_free_node(in);
27553847Seh146360 	}
27563847Seh146360 	else
27573847Seh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
27583847Seh146360 		    "ipw2200_rcv_frame(): "
27593847Seh146360 		    "cannot allocate receive message(%u)\n",
27603847Seh146360 		    LE_16(frame->len)));
27613847Seh146360 }
27623847Seh146360 
27633847Seh146360 static void
ipw2200_rcv_notif(struct ipw2200_softc * sc,struct ipw2200_notif * notif)27643847Seh146360 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif)
27653847Seh146360 {
27663847Seh146360 	struct ieee80211com			*ic = &sc->sc_ic;
27673847Seh146360 	struct ipw2200_notif_association	*assoc;
27683847Seh146360 	struct ipw2200_notif_authentication	*auth;
27693847Seh146360 	uint8_t					*ndata = (uint8_t *)notif;
27703847Seh146360 
27713847Seh146360 	IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
27723847Seh146360 	    "ipw2200_rcv_notif(): type=%u\n", notif->type));
27733847Seh146360 
27743847Seh146360 	ndata += sizeof (struct ipw2200_notif);
27753847Seh146360 	switch (notif->type) {
27763847Seh146360 	case IPW2200_NOTIF_TYPE_ASSOCIATION:
27773847Seh146360 		assoc = (struct ipw2200_notif_association *)ndata;
27783847Seh146360 
27793847Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
27803847Seh146360 		    "ipw2200_rcv_notif(): association=%u,%u\n",
27813847Seh146360 		    assoc->state, assoc->status));
27823847Seh146360 
27833847Seh146360 		switch (assoc->state) {
27843847Seh146360 		case IPW2200_ASSOC_SUCCESS:
27855485Seh146360 			sc->sc_flags |= IPW2200_FLAG_ASSOCIATED;
27863847Seh146360 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
27873847Seh146360 			break;
27883847Seh146360 		case IPW2200_ASSOC_FAIL:
27895485Seh146360 			sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED;
27905485Seh146360 			ieee80211_begin_scan(ic, 1);
27913847Seh146360 			break;
27923847Seh146360 		default:
27933847Seh146360 			break;
27943847Seh146360 		}
27953847Seh146360 		break;
27963847Seh146360 
27973847Seh146360 	case IPW2200_NOTIF_TYPE_AUTHENTICATION:
27983847Seh146360 		auth = (struct ipw2200_notif_authentication *)ndata;
27993847Seh146360 
28003847Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
28013847Seh146360 		    "ipw2200_rcv_notif(): authentication=%u\n", auth->state));
28023847Seh146360 
28033847Seh146360 		switch (auth->state) {
28043847Seh146360 		case IPW2200_AUTH_SUCCESS:
28053847Seh146360 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
28063847Seh146360 			break;
28073847Seh146360 		case IPW2200_AUTH_FAIL:
28085485Seh146360 			sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED;
28093847Seh146360 			break;
28103847Seh146360 		default:
28113847Seh146360 			IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
28123847Seh146360 			    "ipw2200_rcv_notif(): "
28133847Seh146360 			    "unknown authentication state(%u)\n", auth->state));
28143847Seh146360 			break;
28153847Seh146360 		}
28163847Seh146360 		break;
28173847Seh146360 
28183847Seh146360 	case IPW2200_NOTIF_TYPE_SCAN_CHANNEL:
28193847Seh146360 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
28203847Seh146360 		    "ipw2200_rcv_notif(): scan-channel=%u\n",
28213847Seh146360 		    ((struct ipw2200_notif_scan_channel *)ndata)->nchan));
28223847Seh146360 		break;
28233847Seh146360 
28243847Seh146360 	case IPW2200_NOTIF_TYPE_SCAN_COMPLETE:
28253847Seh146360 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
28263847Seh146360 		    "ipw2200_rcv_notif():scan-completed,(%u,%u)\n",
28273847Seh146360 		    ((struct ipw2200_notif_scan_complete *)ndata)->nchan,
28283847Seh146360 		    ((struct ipw2200_notif_scan_complete *)ndata)->status));
28293847Seh146360 
28303847Seh146360 		/*
28313847Seh146360 		 * scan complete
28323847Seh146360 		 */
28333847Seh146360 		sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
28343847Seh146360 		ieee80211_end_scan(ic);
28353847Seh146360 		break;
28363847Seh146360 
28373847Seh146360 	case IPW2200_NOTIF_TYPE_BEACON:
28383847Seh146360 	case IPW2200_NOTIF_TYPE_CALIBRATION:
28393847Seh146360 	case IPW2200_NOTIF_TYPE_NOISE:
28403847Seh146360 		/*
28413847Seh146360 		 * just ignore
28423847Seh146360 		 */
28433847Seh146360 		break;
28443847Seh146360 	default:
28453847Seh146360 		IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
28463847Seh146360 		    "ipw2200_rcv_notif(): unknown notification type(%u)\n",
28473847Seh146360 		    notif->type));
28483847Seh146360 		break;
28493847Seh146360 	}
28503847Seh146360 }
28513847Seh146360 
28523847Seh146360 static uint_t
ipw2200_intr(caddr_t arg)28533847Seh146360 ipw2200_intr(caddr_t arg)
28543847Seh146360 {
28557194Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)(uintptr_t)arg;
28563847Seh146360 	struct ieee80211com	*ic = &sc->sc_ic;
28573847Seh146360 	uint32_t		ireg, ridx, len, i;
28583847Seh146360 	uint8_t			*p, *rxbuf;
28593847Seh146360 	struct dma_region	*dr;
28603847Seh146360 	struct ipw2200_hdr	*hdr;
28613847Seh146360 	uint32_t		widx;
28623847Seh146360 
28637194Seh146360 	/* when it is on suspend, unclaim all interrupt directly */
28647194Seh146360 	if (sc->sc_flags & IPW2200_FLAG_SUSPEND)
28657194Seh146360 		return (DDI_INTR_UNCLAIMED);
28667194Seh146360 
28677194Seh146360 	/* unclaim interrupt when it is not for iwi */
28683847Seh146360 	ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR);
28697194Seh146360 	if (ireg == 0xffffffff ||
28707194Seh146360 	    !(ireg & IPW2200_INTR_MASK_ALL))
28713847Seh146360 		return (DDI_INTR_UNCLAIMED);
28723847Seh146360 
28733847Seh146360 	/*
28743847Seh146360 	 * mask all interrupts
28753847Seh146360 	 */
28763847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
28773847Seh146360 
28783847Seh146360 	/*
28793847Seh146360 	 * acknowledge all fired interrupts
28803847Seh146360 	 */
28813847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg);
28823847Seh146360 
28833847Seh146360 	IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
28843847Seh146360 	    "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg));
28853847Seh146360 
28863847Seh146360 	if (ireg & IPW2200_INTR_MASK_ERR) {
28873847Seh146360 
28883847Seh146360 		IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
28893847Seh146360 		    "ipw2200 interrupt(): int= 0x%08x\n", ireg));
28903847Seh146360 
28913847Seh146360 		/*
28923847Seh146360 		 * inform mfthread to recover hw error by stopping it
28933847Seh146360 		 */
28943847Seh146360 		mutex_enter(&sc->sc_mflock);
28953847Seh146360 		sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER;
28963847Seh146360 		mutex_exit(&sc->sc_mflock);
28973847Seh146360 
28987194Seh146360 		goto enable_interrupt;
28997194Seh146360 	}
29007194Seh146360 
29017194Seh146360 	/*
29027194Seh146360 	 * FW intr
29037194Seh146360 	 */
29047194Seh146360 	if (ireg & IPW2200_INTR_FW_INITED) {
29057194Seh146360 		mutex_enter(&sc->sc_ilock);
29067194Seh146360 		sc->sc_fw_ok = 1;
29077194Seh146360 		cv_signal(&sc->sc_fw_cond);
29087194Seh146360 		mutex_exit(&sc->sc_ilock);
29097194Seh146360 	}
29107194Seh146360 
29117194Seh146360 	/*
29127194Seh146360 	 * Radio OFF
29137194Seh146360 	 */
29147194Seh146360 	if (ireg & IPW2200_INTR_RADIO_OFF) {
29157194Seh146360 		IPW2200_REPORT((sc->sc_dip, CE_CONT,
29167194Seh146360 		    "ipw2200_intr(): radio is OFF\n"));
29175485Seh146360 
29185485Seh146360 		/*
29197194Seh146360 		 * Stop hardware, will notify LINK is down.
29207194Seh146360 		 * Need a better scan solution to ensure
29217194Seh146360 		 * table has right value.
29225485Seh146360 		 */
29237194Seh146360 		ipw2200_stop(sc);
29247194Seh146360 	}
29257194Seh146360 
29267194Seh146360 	/*
29277194Seh146360 	 * CMD intr
29287194Seh146360 	 */
29297194Seh146360 	if (ireg & IPW2200_INTR_CMD_TRANSFER) {
29307194Seh146360 		mutex_enter(&sc->sc_cmd_lock);
29317194Seh146360 		ridx = ipw2200_csr_get32(sc,
29327194Seh146360 		    IPW2200_CSR_CMD_READ_INDEX);
29337194Seh146360 		i = RING_FORWARD(sc->sc_cmd_cur,
29347194Seh146360 		    sc->sc_cmd_free, IPW2200_CMD_RING_SIZE);
29357194Seh146360 		len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE);
29367194Seh146360 
29377194Seh146360 		IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
29387194Seh146360 		    "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n",
29397194Seh146360 		    i, ridx, len));
29407194Seh146360 
29417194Seh146360 		if (len > 0) {
29427194Seh146360 			sc->sc_cmd_free += len;
29437194Seh146360 			cv_signal(&sc->sc_cmd_cond);
29443847Seh146360 		}
29457194Seh146360 		for (; i != ridx;
29467194Seh146360 		    i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE))
29477194Seh146360 			sc->sc_done[i] = 1;
29487194Seh146360 		mutex_exit(&sc->sc_cmd_lock);
29497194Seh146360 
29507194Seh146360 		mutex_enter(&sc->sc_ilock);
29517194Seh146360 		cv_signal(&sc->sc_cmd_status_cond);
29527194Seh146360 		mutex_exit(&sc->sc_ilock);
29537194Seh146360 	}
29547194Seh146360 
29557194Seh146360 	/*
29567194Seh146360 	 * RX intr
29577194Seh146360 	 */
29587194Seh146360 	if (ireg & IPW2200_INTR_RX_TRANSFER) {
29597194Seh146360 		ridx = ipw2200_csr_get32(sc,
29607194Seh146360 		    IPW2200_CSR_RX_READ_INDEX);
29617194Seh146360 		widx = ipw2200_csr_get32(sc,
29627194Seh146360 		    IPW2200_CSR_RX_WRITE_INDEX);
29637194Seh146360 
29647194Seh146360 		IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
29657194Seh146360 		    "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n",
29667194Seh146360 		    ridx, widx));
29677194Seh146360 
29687194Seh146360 		for (; sc->sc_rx_cur != ridx;
29697194Seh146360 		    sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1,
29707194Seh146360 		    IPW2200_RX_RING_SIZE)) {
29717194Seh146360 			i	= sc->sc_rx_cur;
29727194Seh146360 			rxbuf	= sc->sc_rxbufs[i];
29737194Seh146360 			dr	= &sc->sc_dma_rxbufs[i];
29747194Seh146360 
29757194Seh146360 			/*
29767194Seh146360 			 * DMA sync
29777194Seh146360 			 */
29787194Seh146360 			(void) ddi_dma_sync(dr->dr_hnd, 0,
29797194Seh146360 			    IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL);
29807194Seh146360 			/*
29817194Seh146360 			 * Get rx header(hdr) and rx data(p) from rxbuf
29827194Seh146360 			 */
29837194Seh146360 			p	= rxbuf;
29847194Seh146360 			hdr	= (struct ipw2200_hdr *)p;
29857194Seh146360 			p	+= sizeof (struct ipw2200_hdr);
29863847Seh146360 
29873847Seh146360 			IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
29887194Seh146360 			    "ipw2200_intr(): Rx hdr type %u\n",
29897194Seh146360 			    hdr->type));
29907194Seh146360 
29917194Seh146360 			switch (hdr->type) {
29927194Seh146360 			case IPW2200_HDR_TYPE_FRAME:
29937194Seh146360 				ipw2200_rcv_frame(sc,
29947194Seh146360 				    (struct ipw2200_frame *)p);
29957194Seh146360 				break;
29967194Seh146360 
29977194Seh146360 			case IPW2200_HDR_TYPE_NOTIF:
29987194Seh146360 				ipw2200_rcv_notif(sc,
29997194Seh146360 				    (struct ipw2200_notif *)p);
30007194Seh146360 				break;
30017194Seh146360 
30027194Seh146360 			default:
30037194Seh146360 				IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip,
30047194Seh146360 				    CE_CONT,
30057194Seh146360 				    "ipw2200_intr(): unknown Rx hdr type %u\n",
30063847Seh146360 				    hdr->type));
30077194Seh146360 				break;
30083847Seh146360 			}
30093847Seh146360 		}
30105485Seh146360 		/*
30117194Seh146360 		 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX
30125485Seh146360 		 */
30137194Seh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
30147194Seh146360 		    RING_BACKWARD(sc->sc_rx_cur, 1,
30157194Seh146360 		    IPW2200_RX_RING_SIZE));
30167194Seh146360 	}
30177194Seh146360 
30187194Seh146360 	/*
30197194Seh146360 	 * TX intr
30207194Seh146360 	 */
30217194Seh146360 	if (ireg & IPW2200_INTR_TX1_TRANSFER) {
30227194Seh146360 		mutex_enter(&sc->sc_tx_lock);
30237194Seh146360 		ridx = ipw2200_csr_get32(sc,
30247194Seh146360 		    IPW2200_CSR_TX1_READ_INDEX);
30257194Seh146360 		len  = RING_FLEN(RING_FORWARD(sc->sc_tx_cur,
30267194Seh146360 		    sc->sc_tx_free, IPW2200_TX_RING_SIZE),
30277194Seh146360 		    ridx, IPW2200_TX_RING_SIZE);
30287194Seh146360 		sc->sc_tx_free += len;
30297194Seh146360 		IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
30307194Seh146360 		    "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n",
30317194Seh146360 		    ridx, len));
30327194Seh146360 		mutex_exit(&sc->sc_tx_lock);
30337194Seh146360 
30347194Seh146360 		mutex_enter(&sc->sc_resched_lock);
30357194Seh146360 		if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) &&
30367194Seh146360 		    (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) {
30377194Seh146360 			IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip,
30387194Seh146360 			    CE_CONT,
30397194Seh146360 			    "ipw2200_intr(): Need Reschedule!"));
30407194Seh146360 			sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED;
30417194Seh146360 			mac_tx_update(ic->ic_mach);
30423847Seh146360 		}
30437194Seh146360 		mutex_exit(&sc->sc_resched_lock);
30443847Seh146360 	}
30453847Seh146360 
30467194Seh146360 enable_interrupt:
30473847Seh146360 	/*
30483847Seh146360 	 * enable all interrupts
30493847Seh146360 	 */
30503847Seh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL);
30513847Seh146360 
30523847Seh146360 	return (DDI_INTR_CLAIMED);
30533847Seh146360 }
30543847Seh146360 
30553847Seh146360 
30563847Seh146360 /*
30573847Seh146360  *  Module Loading Data & Entry Points
30583847Seh146360  */
30593847Seh146360 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach,
30607809SFei.Feng@Sun.COM     ipw2200_detach, nodev, NULL, D_MP, NULL, ipw2200_quiesce);
30613847Seh146360 
30623847Seh146360 static struct modldrv ipw2200_modldrv = {
30633847Seh146360 	&mod_driverops,
30643847Seh146360 	ipw2200_ident,
30653847Seh146360 	&ipw2200_devops
30663847Seh146360 };
30673847Seh146360 
30683847Seh146360 static struct modlinkage ipw2200_modlinkage = {
30693847Seh146360 	MODREV_1,
30703847Seh146360 	&ipw2200_modldrv,
30713847Seh146360 	NULL
30723847Seh146360 };
30733847Seh146360 
30743847Seh146360 int
_init(void)30753847Seh146360 _init(void)
30763847Seh146360 {
30773847Seh146360 	int status;
30783847Seh146360 
30793847Seh146360 	status = ddi_soft_state_init(&ipw2200_ssp,
30803847Seh146360 	    sizeof (struct ipw2200_softc), 1);
30813847Seh146360 	if (status != DDI_SUCCESS)
30823847Seh146360 		return (status);
30833847Seh146360 
30843847Seh146360 	mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME);
30853847Seh146360 	status = mod_install(&ipw2200_modlinkage);
30863847Seh146360 	if (status != DDI_SUCCESS) {
30873847Seh146360 		mac_fini_ops(&ipw2200_devops);
30883847Seh146360 		ddi_soft_state_fini(&ipw2200_ssp);
30893847Seh146360 	}
30903847Seh146360 
30913847Seh146360 	return (status);
30923847Seh146360 }
30933847Seh146360 
30943847Seh146360 int
_fini(void)30953847Seh146360 _fini(void)
30963847Seh146360 {
30973847Seh146360 	int status;
30983847Seh146360 
30993847Seh146360 	status = mod_remove(&ipw2200_modlinkage);
31003847Seh146360 	if (status == DDI_SUCCESS) {
31013847Seh146360 		mac_fini_ops(&ipw2200_devops);
31023847Seh146360 		ddi_soft_state_fini(&ipw2200_ssp);
31033847Seh146360 	}
31043847Seh146360 
31053847Seh146360 	return (status);
31063847Seh146360 }
31073847Seh146360 
31083847Seh146360 int
_info(struct modinfo * modinfop)31093847Seh146360 _info(struct modinfo *modinfop)
31103847Seh146360 {
31113847Seh146360 	return (mod_info(&ipw2200_modlinkage, modinfop));
31123847Seh146360 }
3113