xref: /illumos-gate/usr/src/uts/common/io/iwi/ipw2200.c (revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac)
1bb5e3b2fSeh146360 /*
2*0dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
3bb5e3b2fSeh146360  * Use is subject to license terms.
4bb5e3b2fSeh146360  */
5bb5e3b2fSeh146360 
6bb5e3b2fSeh146360 /*
7bb5e3b2fSeh146360  * Copyright (c) 2004, 2005
8bb5e3b2fSeh146360  *      Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
9bb5e3b2fSeh146360  *
10bb5e3b2fSeh146360  * Redistribution and use in source and binary forms, with or without
11bb5e3b2fSeh146360  * modification, are permitted provided that the following conditions
12bb5e3b2fSeh146360  * are met:
13bb5e3b2fSeh146360  * 1. Redistributions of source code must retain the above copyright
14bb5e3b2fSeh146360  *    notice unmodified, this list of conditions, and the following
15bb5e3b2fSeh146360  *    disclaimer.
16bb5e3b2fSeh146360  * 2. Redistributions in binary form must reproduce the above copyright
17bb5e3b2fSeh146360  *    notice, this list of conditions and the following disclaimer in the
18bb5e3b2fSeh146360  *    documentation and/or other materials provided with the distribution.
19bb5e3b2fSeh146360  *
20bb5e3b2fSeh146360  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21bb5e3b2fSeh146360  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22bb5e3b2fSeh146360  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23bb5e3b2fSeh146360  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24bb5e3b2fSeh146360  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25bb5e3b2fSeh146360  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26bb5e3b2fSeh146360  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27bb5e3b2fSeh146360  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28bb5e3b2fSeh146360  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29bb5e3b2fSeh146360  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30bb5e3b2fSeh146360  * SUCH DAMAGE.
31bb5e3b2fSeh146360  */
32bb5e3b2fSeh146360 
33bb5e3b2fSeh146360 #include <sys/types.h>
34bb5e3b2fSeh146360 #include <sys/byteorder.h>
35bb5e3b2fSeh146360 #include <sys/conf.h>
36bb5e3b2fSeh146360 #include <sys/cmn_err.h>
37bb5e3b2fSeh146360 #include <sys/stat.h>
38bb5e3b2fSeh146360 #include <sys/ddi.h>
39bb5e3b2fSeh146360 #include <sys/sunddi.h>
40bb5e3b2fSeh146360 #include <sys/strsubr.h>
41bb5e3b2fSeh146360 #include <sys/ethernet.h>
42bb5e3b2fSeh146360 #include <inet/common.h>
43bb5e3b2fSeh146360 #include <inet/nd.h>
44bb5e3b2fSeh146360 #include <inet/mi.h>
45bb5e3b2fSeh146360 #include <sys/note.h>
46bb5e3b2fSeh146360 #include <sys/stream.h>
47bb5e3b2fSeh146360 #include <sys/strsun.h>
48bb5e3b2fSeh146360 #include <sys/modctl.h>
49bb5e3b2fSeh146360 #include <sys/devops.h>
50bb5e3b2fSeh146360 #include <sys/dlpi.h>
51da14cebeSEric Cheng #include <sys/mac_provider.h>
52bb5e3b2fSeh146360 #include <sys/mac_wifi.h>
53bb5e3b2fSeh146360 #include <sys/varargs.h>
54bb5e3b2fSeh146360 #include <sys/pci.h>
55bb5e3b2fSeh146360 #include <sys/policy.h>
56bb5e3b2fSeh146360 #include <sys/random.h>
57924f3e72Seh146360 #include <sys/crypto/common.h>
58924f3e72Seh146360 #include <sys/crypto/api.h>
59bb5e3b2fSeh146360 
60bb5e3b2fSeh146360 #include "ipw2200.h"
61bb5e3b2fSeh146360 #include "ipw2200_impl.h"
62bb5e3b2fSeh146360 #include <inet/wifi_ioctl.h>
63bb5e3b2fSeh146360 
64bb5e3b2fSeh146360 /*
65924f3e72Seh146360  * for net80211 kernel usage
66924f3e72Seh146360  */
67924f3e72Seh146360 #include <sys/net80211.h>
68924f3e72Seh146360 #include <sys/net80211_proto.h>
69924f3e72Seh146360 
70924f3e72Seh146360 /*
71bb5e3b2fSeh146360  * minimal size reserved in tx-ring
72bb5e3b2fSeh146360  */
73bb5e3b2fSeh146360 #define	IPW2200_TX_RING_MIN	(8)
74bb5e3b2fSeh146360 #define	IPW2200_TXBUF_SIZE	(IEEE80211_MAX_LEN)
75bb5e3b2fSeh146360 #define	IPW2200_RXBUF_SIZE	(4096)
76bb5e3b2fSeh146360 
77bb5e3b2fSeh146360 static void  *ipw2200_ssp = NULL;
7819397407SSherry Moore static char ipw2200_ident[] = IPW2200_DRV_DESC;
79bb5e3b2fSeh146360 
80bb5e3b2fSeh146360 /*
81bb5e3b2fSeh146360  * PIO access attributor for registers
82bb5e3b2fSeh146360  */
83bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_csr_accattr = {
84bb5e3b2fSeh146360 	DDI_DEVICE_ATTR_V0,
85bb5e3b2fSeh146360 	DDI_STRUCTURE_LE_ACC,
86bb5e3b2fSeh146360 	DDI_STRICTORDER_ACC
87bb5e3b2fSeh146360 };
88bb5e3b2fSeh146360 
89bb5e3b2fSeh146360 /*
90bb5e3b2fSeh146360  * DMA access attributor for descriptors
91bb5e3b2fSeh146360  */
92bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_dma_accattr = {
93bb5e3b2fSeh146360 	DDI_DEVICE_ATTR_V0,
94bb5e3b2fSeh146360 	DDI_NEVERSWAP_ACC,
95bb5e3b2fSeh146360 	DDI_STRICTORDER_ACC
96bb5e3b2fSeh146360 };
97bb5e3b2fSeh146360 
98bb5e3b2fSeh146360 /*
99bb5e3b2fSeh146360  * Describes the chip's DMA engine
100bb5e3b2fSeh146360  */
101bb5e3b2fSeh146360 static ddi_dma_attr_t ipw2200_dma_attr = {
102bb5e3b2fSeh146360 	DMA_ATTR_V0,		/* version */
103bb5e3b2fSeh146360 	0x0000000000000000ULL,  /* addr_lo */
104bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* addr_hi */
105bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* counter */
106bb5e3b2fSeh146360 	0x0000000000000004ULL,  /* alignment */
107bb5e3b2fSeh146360 	0xfff,			/* burst */
108bb5e3b2fSeh146360 	1,			/* min xfer */
109bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* max xfer */
110bb5e3b2fSeh146360 	0x00000000ffffffffULL,  /* seg boud */
111bb5e3b2fSeh146360 	1,			/* s/g list */
112bb5e3b2fSeh146360 	1,			/* granularity */
113bb5e3b2fSeh146360 	0			/* flags */
114bb5e3b2fSeh146360 };
115bb5e3b2fSeh146360 
116bb5e3b2fSeh146360 static uint8_t ipw2200_broadcast_addr[] = {
117bb5e3b2fSeh146360 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
118bb5e3b2fSeh146360 };
119bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11a = { 8,
120bb5e3b2fSeh146360 	{12, 18, 24, 36, 48, 72, 96, 108}
121bb5e3b2fSeh146360 };
122bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11b = { 4,
123bb5e3b2fSeh146360 	{2, 4, 11, 22}
124bb5e3b2fSeh146360 };
125bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11g = { 12,
126bb5e3b2fSeh146360 	{2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
127bb5e3b2fSeh146360 };
128bb5e3b2fSeh146360 
129bb5e3b2fSeh146360 /*
130bb5e3b2fSeh146360  * Used by multi function thread
131bb5e3b2fSeh146360  */
132bb5e3b2fSeh146360 extern pri_t minclsyspri;
133bb5e3b2fSeh146360 
134bb5e3b2fSeh146360 /*
135bb5e3b2fSeh146360  * ipw2200 specific hardware operations
136bb5e3b2fSeh146360  */
137bb5e3b2fSeh146360 static void	ipw2200_hwconf_get(struct ipw2200_softc *sc);
138bb5e3b2fSeh146360 static int	ipw2200_chip_reset(struct ipw2200_softc *sc);
139bb5e3b2fSeh146360 static void	ipw2200_master_stop(struct ipw2200_softc *sc);
140bb5e3b2fSeh146360 static void	ipw2200_stop(struct ipw2200_softc *sc);
141bb5e3b2fSeh146360 static int	ipw2200_config(struct ipw2200_softc *sc);
142bb5e3b2fSeh146360 static int	ipw2200_cmd(struct ipw2200_softc *sc,
143bb5e3b2fSeh146360     uint32_t type, void *buf, size_t len, int async);
144bb5e3b2fSeh146360 static void	ipw2200_ring_hwsetup(struct ipw2200_softc *sc);
145bb5e3b2fSeh146360 static int	ipw2200_ring_alloc(struct ipw2200_softc *sc);
146bb5e3b2fSeh146360 static void	ipw2200_ring_free(struct ipw2200_softc *sc);
147bb5e3b2fSeh146360 static void	ipw2200_ring_reset(struct ipw2200_softc *sc);
148bb5e3b2fSeh146360 static int	ipw2200_ring_init(struct ipw2200_softc *sc);
149bb5e3b2fSeh146360 
150bb5e3b2fSeh146360 /*
151bb5e3b2fSeh146360  * GLD specific operations
152bb5e3b2fSeh146360  */
153bb5e3b2fSeh146360 static int	ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val);
154bb5e3b2fSeh146360 static int	ipw2200_m_start(void *arg);
155bb5e3b2fSeh146360 static void	ipw2200_m_stop(void *arg);
156bb5e3b2fSeh146360 static int	ipw2200_m_unicst(void *arg, const uint8_t *macaddr);
157bb5e3b2fSeh146360 static int	ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *m);
158bb5e3b2fSeh146360 static int	ipw2200_m_promisc(void *arg, boolean_t on);
159bb5e3b2fSeh146360 static void	ipw2200_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
160bb5e3b2fSeh146360 static mblk_t  *ipw2200_m_tx(void *arg, mblk_t *mp);
1617efa17f5Sfei feng - Sun Microsystems - Beijing China static int	ipw2200_m_setprop(void *arg, const char *pr_name,
1627efa17f5Sfei feng - Sun Microsystems - Beijing China     mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
1637efa17f5Sfei feng - Sun Microsystems - Beijing China static int	ipw2200_m_getprop(void *arg, const char *pr_name,
164*0dc2366fSVenugopal Iyer     mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
165*0dc2366fSVenugopal Iyer static void	ipw2200_m_propinfo(void *arg, const char *pr_name,
166*0dc2366fSVenugopal Iyer     mac_prop_id_t wldp_pr_num, mac_prop_info_handle_t mph);
167bb5e3b2fSeh146360 
168bb5e3b2fSeh146360 /*
169bb5e3b2fSeh146360  * Interrupt and Data transferring operations
170bb5e3b2fSeh146360  */
171bb5e3b2fSeh146360 static uint_t	ipw2200_intr(caddr_t arg);
172bb5e3b2fSeh146360 static int	ipw2200_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type);
173bb5e3b2fSeh146360 static void	ipw2200_rcv_frame(struct ipw2200_softc *sc,
174bb5e3b2fSeh146360     struct ipw2200_frame *frame);
175bb5e3b2fSeh146360 static void	ipw2200_rcv_notif(struct ipw2200_softc *sc,
176bb5e3b2fSeh146360     struct ipw2200_notif *notif);
177bb5e3b2fSeh146360 
178bb5e3b2fSeh146360 /*
179bb5e3b2fSeh146360  * WiFi specific operations
180bb5e3b2fSeh146360  */
181bb5e3b2fSeh146360 static int	ipw2200_newstate(struct ieee80211com *ic,
182bb5e3b2fSeh146360     enum ieee80211_state state, int arg);
183bb5e3b2fSeh146360 static void	ipw2200_thread(struct ipw2200_softc *sc);
184bb5e3b2fSeh146360 
185bb5e3b2fSeh146360 /*
186bb5e3b2fSeh146360  * IOCTL Handler
187bb5e3b2fSeh146360  */
188bb5e3b2fSeh146360 static int	ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m);
189bb5e3b2fSeh146360 static int	ipw2200_getset(struct ipw2200_softc *sc,
190bb5e3b2fSeh146360     mblk_t *m, uint32_t cmd, boolean_t *need_net80211);
191bb5e3b2fSeh146360 static int	iwi_wificfg_radio(struct ipw2200_softc *sc,
192bb5e3b2fSeh146360     uint32_t cmd,  wldp_t *outfp);
193bb5e3b2fSeh146360 static int	iwi_wificfg_desrates(wldp_t *outfp);
194bb5e3b2fSeh146360 
195bb5e3b2fSeh146360 /*
196924f3e72Seh146360  * net80211 functions
197924f3e72Seh146360  */
198924f3e72Seh146360 extern uint8_t	ieee80211_crypto_getciphertype(ieee80211com_t *ic);
199924f3e72Seh146360 extern void	ieee80211_notify_node_join(ieee80211com_t *ic,
200924f3e72Seh146360     ieee80211_node_t *in);
201924f3e72Seh146360 extern void	ieee80211_notify_node_leave(ieee80211com_t *ic,
202924f3e72Seh146360     ieee80211_node_t *in);
203924f3e72Seh146360 
204924f3e72Seh146360 /*
205bb5e3b2fSeh146360  * Mac Call Back entries
206bb5e3b2fSeh146360  */
207bb5e3b2fSeh146360 mac_callbacks_t	ipw2200_m_callbacks = {
208*0dc2366fSVenugopal Iyer 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
209bb5e3b2fSeh146360 	ipw2200_m_stat,
210bb5e3b2fSeh146360 	ipw2200_m_start,
211bb5e3b2fSeh146360 	ipw2200_m_stop,
212bb5e3b2fSeh146360 	ipw2200_m_promisc,
213bb5e3b2fSeh146360 	ipw2200_m_multicst,
214bb5e3b2fSeh146360 	ipw2200_m_unicst,
215bb5e3b2fSeh146360 	ipw2200_m_tx,
216*0dc2366fSVenugopal Iyer 	NULL,
2177efa17f5Sfei feng - Sun Microsystems - Beijing China 	ipw2200_m_ioctl,
2187efa17f5Sfei feng - Sun Microsystems - Beijing China 	NULL,
2197efa17f5Sfei feng - Sun Microsystems - Beijing China 	NULL,
2207efa17f5Sfei feng - Sun Microsystems - Beijing China 	NULL,
2217efa17f5Sfei feng - Sun Microsystems - Beijing China 	ipw2200_m_setprop,
222*0dc2366fSVenugopal Iyer 	ipw2200_m_getprop,
223*0dc2366fSVenugopal Iyer 	ipw2200_m_propinfo
224bb5e3b2fSeh146360 };
225bb5e3b2fSeh146360 
226bb5e3b2fSeh146360 /*
227bb5e3b2fSeh146360  * DEBUG Facility
228bb5e3b2fSeh146360  */
229bb5e3b2fSeh146360 #define		MAX_MSG		(128)
230bb5e3b2fSeh146360 uint32_t	ipw2200_debug = 0;
231bb5e3b2fSeh146360 /*
232bb5e3b2fSeh146360  * supported debug marks are:
233bb5e3b2fSeh146360  *	| IPW2200_DBG_CSR
234bb5e3b2fSeh146360  *	| IPW2200_DBG_TABLE
235bb5e3b2fSeh146360  *	| IPW2200_DBG_HWCAP
236bb5e3b2fSeh146360  *	| IPW2200_DBG_TX
237bb5e3b2fSeh146360  *	| IPW2200_DBG_INIT
238bb5e3b2fSeh146360  *	| IPW2200_DBG_FW
239bb5e3b2fSeh146360  *	| IPW2200_DBG_NOTIF
240bb5e3b2fSeh146360  *	| IPW2200_DBG_SCAN
241bb5e3b2fSeh146360  *	| IPW2200_DBG_IOCTL
242bb5e3b2fSeh146360  *	| IPW2200_DBG_RING
243bb5e3b2fSeh146360  *	| IPW2200_DBG_INT
244bb5e3b2fSeh146360  *	| IPW2200_DBG_RX
245bb5e3b2fSeh146360  *	| IPW2200_DBG_DMA
246bb5e3b2fSeh146360  *	| IPW2200_DBG_GLD
247bb5e3b2fSeh146360  *	| IPW2200_DBG_WIFI
248bb5e3b2fSeh146360  *	| IPW2200_DBG_SOFTINT
249922d2c76Seh146360  *	| IPW2200_DBG_SUSPEND
2507efa17f5Sfei feng - Sun Microsystems - Beijing China  *	| IPW2200_DBG_BRUSSELS
251bb5e3b2fSeh146360  */
252bb5e3b2fSeh146360 
253bb5e3b2fSeh146360 /*
254bb5e3b2fSeh146360  * Global tunning parameter to work around unknown hardware issues
255bb5e3b2fSeh146360  */
256bb5e3b2fSeh146360 static uint32_t delay_config_stable	= 100000;	/* 100ms */
257bb5e3b2fSeh146360 static uint32_t delay_fatal_recover	= 100000 * 20;	/* 2s */
258bb5e3b2fSeh146360 static uint32_t delay_aux_thread	= 100000;	/* 100ms */
259bb5e3b2fSeh146360 
260bb5e3b2fSeh146360 #define	IEEE80211_IS_CHAN_2GHZ(_c) \
261bb5e3b2fSeh146360 	(((_c)->ich_flags & IEEE80211_CHAN_2GHZ) != 0)
262bb5e3b2fSeh146360 #define	IEEE80211_IS_CHAN_5GHZ(_c) \
263bb5e3b2fSeh146360 	(((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0)
264bb5e3b2fSeh146360 #define	isset(a, i)	((a)[(i)/NBBY] & (1 << ((i)%NBBY)))
265bb5e3b2fSeh146360 
266bb5e3b2fSeh146360 void
ipw2200_dbg(dev_info_t * dip,int level,const char * fmt,...)267bb5e3b2fSeh146360 ipw2200_dbg(dev_info_t *dip, int level, const char *fmt, ...)
268bb5e3b2fSeh146360 {
269bb5e3b2fSeh146360 	va_list	ap;
270bb5e3b2fSeh146360 	char    buf[MAX_MSG];
271bb5e3b2fSeh146360 	int	instance;
272bb5e3b2fSeh146360 
273bb5e3b2fSeh146360 	va_start(ap, fmt);
274bb5e3b2fSeh146360 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
275bb5e3b2fSeh146360 	va_end(ap);
276bb5e3b2fSeh146360 
277bb5e3b2fSeh146360 	if (dip) {
278bb5e3b2fSeh146360 		instance = ddi_get_instance(dip);
279bb5e3b2fSeh146360 		cmn_err(level, "%s%d: %s", IPW2200_DRV_NAME, instance, buf);
280bb5e3b2fSeh146360 	} else
281bb5e3b2fSeh146360 		cmn_err(level, "%s: %s", IPW2200_DRV_NAME, buf);
282bb5e3b2fSeh146360 
283bb5e3b2fSeh146360 }
284bb5e3b2fSeh146360 
285bb5e3b2fSeh146360 /*
286922d2c76Seh146360  * Set up pci
287922d2c76Seh146360  */
288922d2c76Seh146360 int
ipw2200_setup_pci(dev_info_t * dip,struct ipw2200_softc * sc)289922d2c76Seh146360 ipw2200_setup_pci(dev_info_t *dip, struct ipw2200_softc *sc)
290922d2c76Seh146360 {
291922d2c76Seh146360 	ddi_acc_handle_t	cfgh;
292922d2c76Seh146360 	caddr_t			regs;
293922d2c76Seh146360 	int			err;
294922d2c76Seh146360 
295922d2c76Seh146360 	/*
296922d2c76Seh146360 	 * Map config spaces register to read the vendor id, device id, sub
297922d2c76Seh146360 	 * vendor id, and sub device id.
298922d2c76Seh146360 	 */
299922d2c76Seh146360 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CFG_RNUM, &regs,
300922d2c76Seh146360 	    0, 0, &ipw2200_csr_accattr, &cfgh);
301922d2c76Seh146360 	if (err != DDI_SUCCESS) {
302922d2c76Seh146360 		IPW2200_WARN((dip, CE_WARN,
303922d2c76Seh146360 		    "ipw2200_attach(): unable to map spaces regs\n"));
304922d2c76Seh146360 		return (DDI_FAILURE);
305922d2c76Seh146360 	}
306922d2c76Seh146360 
307922d2c76Seh146360 	ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0);
308922d2c76Seh146360 	sc->sc_vendor = ddi_get16(cfgh,
309922d2c76Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_VENID));
310922d2c76Seh146360 	sc->sc_device = ddi_get16(cfgh,
311922d2c76Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_DEVID));
312922d2c76Seh146360 	sc->sc_subven = ddi_get16(cfgh,
313922d2c76Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBVENID));
314922d2c76Seh146360 	sc->sc_subdev = ddi_get16(cfgh,
315922d2c76Seh146360 	    (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBSYSID));
316922d2c76Seh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
317922d2c76Seh146360 	    "ipw2200_setup_pci(): vendor = 0x%04x, devic = 0x%04x,"
318922d2c76Seh146360 	    "subversion = 0x%04x, subdev = 0x%04x",
319922d2c76Seh146360 	    sc->sc_vendor, sc->sc_device, sc->sc_subven, sc->sc_subdev));
320922d2c76Seh146360 
321922d2c76Seh146360 	ddi_regs_map_free(&cfgh);
322922d2c76Seh146360 
323922d2c76Seh146360 	return (DDI_SUCCESS);
324922d2c76Seh146360 
325922d2c76Seh146360 }
326922d2c76Seh146360 
327922d2c76Seh146360 /*
328bb5e3b2fSeh146360  * Device operations
329bb5e3b2fSeh146360  */
330bb5e3b2fSeh146360 int
ipw2200_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)331bb5e3b2fSeh146360 ipw2200_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
332bb5e3b2fSeh146360 {
333bb5e3b2fSeh146360 	struct ipw2200_softc	*sc;
334bb5e3b2fSeh146360 	struct ieee80211com	*ic;
335bb5e3b2fSeh146360 	int			instance, err, i;
336bb5e3b2fSeh146360 	char			strbuf[32];
337bb5e3b2fSeh146360 	wifi_data_t		wd = { 0 };
338bb5e3b2fSeh146360 	mac_register_t		*macp;
339bb5e3b2fSeh146360 
340922d2c76Seh146360 	switch (cmd) {
341922d2c76Seh146360 	case DDI_ATTACH:
342922d2c76Seh146360 		break;
343922d2c76Seh146360 	case DDI_RESUME:
344922d2c76Seh146360 		sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
345922d2c76Seh146360 		ASSERT(sc != NULL);
346922d2c76Seh146360 
347922d2c76Seh146360 		/*
348922d2c76Seh146360 		 * set up pci
349922d2c76Seh146360 		 */
350922d2c76Seh146360 		err = ipw2200_setup_pci(dip, sc);
351922d2c76Seh146360 		if (err != DDI_SUCCESS) {
352922d2c76Seh146360 			IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
353922d2c76Seh146360 			    "ipw2200_attach(): resume failure\n"));
354922d2c76Seh146360 			return (DDI_FAILURE);
355922d2c76Seh146360 		}
356922d2c76Seh146360 
357922d2c76Seh146360 		/*
358922d2c76Seh146360 		 * resume hardware.
359922d2c76Seh146360 		 * If it was on runnning status, reset to INIT state
360922d2c76Seh146360 		 */
361922d2c76Seh146360 		sc->sc_flags &= ~IPW2200_FLAG_SUSPEND;
362922d2c76Seh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING)
363922d2c76Seh146360 			(void) ipw2200_init(sc);
364922d2c76Seh146360 
365922d2c76Seh146360 		IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
366922d2c76Seh146360 		    "ipw2200_attach(): resume successful\n"));
367922d2c76Seh146360 		return (DDI_SUCCESS);
368922d2c76Seh146360 	default:
369922d2c76Seh146360 		return (DDI_FAILURE);
370bb5e3b2fSeh146360 	}
371bb5e3b2fSeh146360 
372bb5e3b2fSeh146360 	instance = ddi_get_instance(dip);
373bb5e3b2fSeh146360 	err = ddi_soft_state_zalloc(ipw2200_ssp, instance);
374bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
375bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
376bb5e3b2fSeh146360 		    "ipw2200_attach(): unable to allocate soft state\n"));
377bb5e3b2fSeh146360 		goto fail1;
378bb5e3b2fSeh146360 	}
379bb5e3b2fSeh146360 	sc = ddi_get_soft_state(ipw2200_ssp, instance);
380bb5e3b2fSeh146360 	sc->sc_dip = dip;
381bb5e3b2fSeh146360 
382922d2c76Seh146360 	/* set up pci, put reg+0x41 0 */
383922d2c76Seh146360 	err = ipw2200_setup_pci(dip, sc);
384bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
385bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
386922d2c76Seh146360 		    "ipw2200_attach(): unable to setup pci\n"));
387bb5e3b2fSeh146360 		goto fail2;
388bb5e3b2fSeh146360 	}
389bb5e3b2fSeh146360 
390bb5e3b2fSeh146360 	/*
391bb5e3b2fSeh146360 	 * Map operating registers
392bb5e3b2fSeh146360 	 */
393bb5e3b2fSeh146360 	err = ddi_regs_map_setup(dip, IPW2200_PCI_CSR_RNUM, &sc->sc_regs,
394bb5e3b2fSeh146360 	    0, 0, &ipw2200_csr_accattr, &sc->sc_ioh);
395bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
396bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
397bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_regs_map_setup() failed\n"));
398bb5e3b2fSeh146360 		goto fail2;
399bb5e3b2fSeh146360 	}
400bb5e3b2fSeh146360 
401bb5e3b2fSeh146360 	/*
402bb5e3b2fSeh146360 	 * Reset the chip
403bb5e3b2fSeh146360 	 */
404bb5e3b2fSeh146360 	err = ipw2200_chip_reset(sc);
405bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
406bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
407bb5e3b2fSeh146360 		    "ipw2200_attach(): ipw2200_chip_reset() failed\n"));
408bb5e3b2fSeh146360 		goto fail3;
409bb5e3b2fSeh146360 	}
410bb5e3b2fSeh146360 
411bb5e3b2fSeh146360 	/*
412bb5e3b2fSeh146360 	 * Get the hardware configuration, including the MAC address
413bb5e3b2fSeh146360 	 * Then, init all the rings needed.
414bb5e3b2fSeh146360 	 */
415bb5e3b2fSeh146360 	ipw2200_hwconf_get(sc);
416bb5e3b2fSeh146360 	err = ipw2200_ring_init(sc);
417bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
418bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
419bb5e3b2fSeh146360 		    "ipw2200_attach(): ipw2200_ring_init() failed\n"));
420bb5e3b2fSeh146360 		goto fail3;
421bb5e3b2fSeh146360 	}
422bb5e3b2fSeh146360 
423bb5e3b2fSeh146360 	/*
424bb5e3b2fSeh146360 	 * Initialize mutexs and condvars
425bb5e3b2fSeh146360 	 */
426bb5e3b2fSeh146360 	err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk);
427bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
428bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
429bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_get_iblock_cookie() failed\n"));
430bb5e3b2fSeh146360 		goto fail4;
431bb5e3b2fSeh146360 	}
432bb5e3b2fSeh146360 
433bb5e3b2fSeh146360 	/*
434bb5e3b2fSeh146360 	 * interrupt lock
435bb5e3b2fSeh146360 	 */
436bb5e3b2fSeh146360 	mutex_init(&sc->sc_ilock, "intr-lock", MUTEX_DRIVER,
437bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
438bb5e3b2fSeh146360 	cv_init(&sc->sc_fw_cond, "firmware-ok", CV_DRIVER, NULL);
439bb5e3b2fSeh146360 	cv_init(&sc->sc_cmd_status_cond, "cmd-status-ring", CV_DRIVER, NULL);
440bb5e3b2fSeh146360 
441bb5e3b2fSeh146360 	/*
442bb5e3b2fSeh146360 	 * command ring lock
443bb5e3b2fSeh146360 	 */
444bb5e3b2fSeh146360 	mutex_init(&sc->sc_cmd_lock, "cmd-ring", MUTEX_DRIVER,
445bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
446bb5e3b2fSeh146360 	cv_init(&sc->sc_cmd_cond, "cmd-ring", CV_DRIVER, NULL);
447bb5e3b2fSeh146360 
448bb5e3b2fSeh146360 	/*
449bb5e3b2fSeh146360 	 * tx ring lock
450bb5e3b2fSeh146360 	 */
451bb5e3b2fSeh146360 	mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER,
452bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
453bb5e3b2fSeh146360 
454bb5e3b2fSeh146360 	/*
455924f3e72Seh146360 	 * rescheduled lock
456924f3e72Seh146360 	 */
457924f3e72Seh146360 	mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER,
458924f3e72Seh146360 	    (void *) sc->sc_iblk);
459924f3e72Seh146360 
460924f3e72Seh146360 	/*
461bb5e3b2fSeh146360 	 * multi-function lock, may acquire this during interrupt
462bb5e3b2fSeh146360 	 */
463bb5e3b2fSeh146360 	mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER,
464bb5e3b2fSeh146360 	    (void *) sc->sc_iblk);
465bb5e3b2fSeh146360 	cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL);
466bb5e3b2fSeh146360 	sc->sc_mf_thread = NULL;
467bb5e3b2fSeh146360 	sc->sc_mfthread_switch = 0;
468bb5e3b2fSeh146360 
469bb5e3b2fSeh146360 	/*
470924f3e72Seh146360 	 * Initialize the WiFi part
471bb5e3b2fSeh146360 	 */
472bb5e3b2fSeh146360 	ic = &sc->sc_ic;
473bb5e3b2fSeh146360 	ic->ic_phytype  = IEEE80211_T_OFDM;
474bb5e3b2fSeh146360 	ic->ic_opmode   = IEEE80211_M_STA;
475bb5e3b2fSeh146360 	ic->ic_state    = IEEE80211_S_INIT;
476bb5e3b2fSeh146360 	ic->ic_maxrssi  = 100; /* experimental number */
477924f3e72Seh146360 	ic->ic_caps =
478924f3e72Seh146360 	    IEEE80211_C_SHPREAMBLE |
479924f3e72Seh146360 	    IEEE80211_C_TXPMGT |
480924f3e72Seh146360 	    IEEE80211_C_PMGT |
481924f3e72Seh146360 	    IEEE80211_C_WPA;
482bb5e3b2fSeh146360 
483bb5e3b2fSeh146360 	/*
484bb5e3b2fSeh146360 	 * set mac addr
485bb5e3b2fSeh146360 	 */
486bb5e3b2fSeh146360 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr);
487bb5e3b2fSeh146360 
488bb5e3b2fSeh146360 	/*
489bb5e3b2fSeh146360 	 * set supported .11a rates and channel - (2915ABG only)
490bb5e3b2fSeh146360 	 */
491922d2c76Seh146360 	if (sc->sc_device >= 0x4223) {
492bb5e3b2fSeh146360 		/* .11a rates */
493bb5e3b2fSeh146360 		ic->ic_sup_rates[IEEE80211_MODE_11A] = ipw2200_rateset_11a;
494bb5e3b2fSeh146360 		/* .11a channels */
495bb5e3b2fSeh146360 		for (i = 36; i <= 64; i += 4) {
496bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_freq =
497bb5e3b2fSeh146360 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
498bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
499bb5e3b2fSeh146360 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
500bb5e3b2fSeh146360 		}
501bb5e3b2fSeh146360 		for (i = 149; i <= 165; i += 4) {
502bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_freq =
503bb5e3b2fSeh146360 			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
504bb5e3b2fSeh146360 			ic->ic_sup_channels[i].ich_flags = /* CHAN_A */
505bb5e3b2fSeh146360 			    IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM;
506bb5e3b2fSeh146360 		}
507bb5e3b2fSeh146360 	}
508bb5e3b2fSeh146360 
509bb5e3b2fSeh146360 	/*
510bb5e3b2fSeh146360 	 * set supported .11b and .11g rates
511bb5e3b2fSeh146360 	 */
512bb5e3b2fSeh146360 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2200_rateset_11b;
513bb5e3b2fSeh146360 	ic->ic_sup_rates[IEEE80211_MODE_11G] = ipw2200_rateset_11g;
514bb5e3b2fSeh146360 
515bb5e3b2fSeh146360 	/*
516bb5e3b2fSeh146360 	 * set supported .11b and .11g channels(1 through 14)
517bb5e3b2fSeh146360 	 */
518bb5e3b2fSeh146360 	for (i = 1; i < 14; i++) {
519bb5e3b2fSeh146360 		ic->ic_sup_channels[i].ich_freq  =
520bb5e3b2fSeh146360 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
521bb5e3b2fSeh146360 		ic->ic_sup_channels[i].ich_flags =
522bb5e3b2fSeh146360 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
523bb5e3b2fSeh146360 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
524bb5e3b2fSeh146360 	}
525bb5e3b2fSeh146360 
526bb5e3b2fSeh146360 	/*
527bb5e3b2fSeh146360 	 * IBSS channal undefined for now
528bb5e3b2fSeh146360 	 */
529bb5e3b2fSeh146360 	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
530bb5e3b2fSeh146360 	ic->ic_xmit = ipw2200_send;
531bb5e3b2fSeh146360 
532bb5e3b2fSeh146360 	/*
533bb5e3b2fSeh146360 	 * init generic layer, then override state transition machine
534bb5e3b2fSeh146360 	 */
535bb5e3b2fSeh146360 	ieee80211_attach(ic);
536bb5e3b2fSeh146360 
537bb5e3b2fSeh146360 	/*
538924f3e72Seh146360 	 * different instance has different WPA door
539924f3e72Seh146360 	 */
540924f3e72Seh146360 	ieee80211_register_door(ic, ddi_driver_name(dip), instance);
541924f3e72Seh146360 
542924f3e72Seh146360 	/*
543bb5e3b2fSeh146360 	 * Override 80211 default routines
544bb5e3b2fSeh146360 	 */
545bb5e3b2fSeh146360 	ieee80211_media_init(ic); /* initial the node table and bss */
546bb5e3b2fSeh146360 	sc->sc_newstate = ic->ic_newstate;
547bb5e3b2fSeh146360 	ic->ic_newstate = ipw2200_newstate;
548bb5e3b2fSeh146360 	ic->ic_def_txkey = 0;
549bb5e3b2fSeh146360 	sc->sc_authmode = IEEE80211_AUTH_OPEN;
550bb5e3b2fSeh146360 
551bb5e3b2fSeh146360 	/*
552bb5e3b2fSeh146360 	 * Add the interrupt handler
553bb5e3b2fSeh146360 	 */
554bb5e3b2fSeh146360 	err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL,
555bb5e3b2fSeh146360 	    ipw2200_intr, (caddr_t)sc);
556bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
557bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
558bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_add_intr() failed\n"));
559bb5e3b2fSeh146360 		goto fail5;
560bb5e3b2fSeh146360 	}
561bb5e3b2fSeh146360 
562bb5e3b2fSeh146360 	/*
563bb5e3b2fSeh146360 	 * Initialize pointer to device specific functions
564bb5e3b2fSeh146360 	 */
565bb5e3b2fSeh146360 	wd.wd_secalloc = WIFI_SEC_NONE;
566bb5e3b2fSeh146360 	wd.wd_opmode = ic->ic_opmode;
567924f3e72Seh146360 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
568bb5e3b2fSeh146360 
569bb5e3b2fSeh146360 	macp = mac_alloc(MAC_VERSION);
570bb5e3b2fSeh146360 	if (err != 0) {
571bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
572bb5e3b2fSeh146360 		    "ipw2200_attach(): mac_alloc() failed\n"));
573bb5e3b2fSeh146360 		goto fail6;
574bb5e3b2fSeh146360 	}
575bb5e3b2fSeh146360 
576bb5e3b2fSeh146360 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
577bb5e3b2fSeh146360 	macp->m_driver		= sc;
578bb5e3b2fSeh146360 	macp->m_dip		= dip;
579bb5e3b2fSeh146360 	macp->m_src_addr	= ic->ic_macaddr;
580bb5e3b2fSeh146360 	macp->m_callbacks	= &ipw2200_m_callbacks;
581bb5e3b2fSeh146360 	macp->m_min_sdu		= 0;
582bb5e3b2fSeh146360 	macp->m_max_sdu		= IEEE80211_MTU;
583bb5e3b2fSeh146360 	macp->m_pdata		= &wd;
584bb5e3b2fSeh146360 	macp->m_pdata_size	= sizeof (wd);
585bb5e3b2fSeh146360 
586bb5e3b2fSeh146360 	/*
587bb5e3b2fSeh146360 	 * Register the macp to mac
588bb5e3b2fSeh146360 	 */
589bb5e3b2fSeh146360 	err = mac_register(macp, &ic->ic_mach);
590bb5e3b2fSeh146360 	mac_free(macp);
591bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
592bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
593bb5e3b2fSeh146360 		    "ipw2200_attach(): mac_register() failed\n"));
594bb5e3b2fSeh146360 		goto fail6;
595bb5e3b2fSeh146360 	}
596bb5e3b2fSeh146360 
597bb5e3b2fSeh146360 	/*
598bb5e3b2fSeh146360 	 * Create minor node of type DDI_NT_NET_WIFI
599bb5e3b2fSeh146360 	 */
600bb5e3b2fSeh146360 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
601bb5e3b2fSeh146360 	    IPW2200_DRV_NAME, instance);
602bb5e3b2fSeh146360 	err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
603bb5e3b2fSeh146360 	    instance + 1, DDI_NT_NET_WIFI, 0);
604bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
605bb5e3b2fSeh146360 		IPW2200_WARN((dip, CE_WARN,
606bb5e3b2fSeh146360 		    "ipw2200_attach(): ddi_create_minor_node() failed\n"));
607bb5e3b2fSeh146360 
608bb5e3b2fSeh146360 	/*
609bb5e3b2fSeh146360 	 * Cache firmware will always be true
610bb5e3b2fSeh146360 	 */
611bb5e3b2fSeh146360 	(void) ipw2200_cache_firmware(sc);
612bb5e3b2fSeh146360 
613bb5e3b2fSeh146360 	/*
614bb5e3b2fSeh146360 	 * Notify link is down now
615bb5e3b2fSeh146360 	 */
616bb5e3b2fSeh146360 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
617bb5e3b2fSeh146360 
618bb5e3b2fSeh146360 	/*
619bb5e3b2fSeh146360 	 * Create the mf thread to handle the link status,
620bb5e3b2fSeh146360 	 * recovery fatal error, etc.
621bb5e3b2fSeh146360 	 */
622bb5e3b2fSeh146360 	sc->sc_mfthread_switch = 1;
623bb5e3b2fSeh146360 	if (sc->sc_mf_thread == NULL)
624bb5e3b2fSeh146360 		sc->sc_mf_thread = thread_create((caddr_t)NULL, 0,
625bb5e3b2fSeh146360 		    ipw2200_thread, sc, 0, &p0, TS_RUN, minclsyspri);
626bb5e3b2fSeh146360 
627bb5e3b2fSeh146360 	return (DDI_SUCCESS);
628bb5e3b2fSeh146360 
629bb5e3b2fSeh146360 fail6:
630bb5e3b2fSeh146360 	ddi_remove_intr(dip, 0, sc->sc_iblk);
631bb5e3b2fSeh146360 fail5:
632bb5e3b2fSeh146360 	ieee80211_detach(ic);
633bb5e3b2fSeh146360 
634bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_ilock);
635bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_cmd_lock);
636bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_tx_lock);
637bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_mflock);
638924f3e72Seh146360 	mutex_destroy(&sc->sc_resched_lock);
639bb5e3b2fSeh146360 	cv_destroy(&sc->sc_fw_cond);
640bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_status_cond);
641bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_cond);
642bb5e3b2fSeh146360 	cv_destroy(&sc->sc_mfthread_cv);
643bb5e3b2fSeh146360 fail4:
644bb5e3b2fSeh146360 	ipw2200_ring_free(sc);
645bb5e3b2fSeh146360 fail3:
646bb5e3b2fSeh146360 	ddi_regs_map_free(&sc->sc_ioh);
647bb5e3b2fSeh146360 fail2:
648bb5e3b2fSeh146360 	ddi_soft_state_free(ipw2200_ssp, instance);
649bb5e3b2fSeh146360 fail1:
650bb5e3b2fSeh146360 	return (err);
651bb5e3b2fSeh146360 }
652bb5e3b2fSeh146360 
653bb5e3b2fSeh146360 
654bb5e3b2fSeh146360 int
ipw2200_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)655bb5e3b2fSeh146360 ipw2200_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
656bb5e3b2fSeh146360 {
657922d2c76Seh146360 	struct ipw2200_softc	*sc;
658bb5e3b2fSeh146360 	int			err;
659922d2c76Seh146360 
660922d2c76Seh146360 	sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
661bb5e3b2fSeh146360 	ASSERT(sc != NULL);
662bb5e3b2fSeh146360 
663922d2c76Seh146360 	switch (cmd) {
664922d2c76Seh146360 	case DDI_DETACH:
665922d2c76Seh146360 		break;
666922d2c76Seh146360 	case DDI_SUSPEND:
667922d2c76Seh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
668922d2c76Seh146360 			ipw2200_stop(sc);
669922d2c76Seh146360 		}
670922d2c76Seh146360 		sc->sc_flags |= IPW2200_FLAG_SUSPEND;
671922d2c76Seh146360 
672922d2c76Seh146360 		IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
673922d2c76Seh146360 		    "ipw2200_detach(): suspend\n"));
674922d2c76Seh146360 		return (DDI_SUCCESS);
675922d2c76Seh146360 	default:
676bb5e3b2fSeh146360 		return (DDI_FAILURE);
677922d2c76Seh146360 	}
678bb5e3b2fSeh146360 
67942516a0cSxinghua wen - Sun Microsystems - Beijing China 	err = mac_disable(sc->sc_ic.ic_mach);
68042516a0cSxinghua wen - Sun Microsystems - Beijing China 	if (err != DDI_SUCCESS)
68142516a0cSxinghua wen - Sun Microsystems - Beijing China 		return (err);
68242516a0cSxinghua wen - Sun Microsystems - Beijing China 
683bb5e3b2fSeh146360 	ipw2200_stop(sc);
684bb5e3b2fSeh146360 
685bb5e3b2fSeh146360 	/*
686bb5e3b2fSeh146360 	 * Destroy the mf_thread
687bb5e3b2fSeh146360 	 */
688bb5e3b2fSeh146360 	mutex_enter(&sc->sc_mflock);
689bb5e3b2fSeh146360 	sc->sc_mfthread_switch = 0;
690bb5e3b2fSeh146360 	while (sc->sc_mf_thread != NULL) {
691bb5e3b2fSeh146360 		if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0)
692bb5e3b2fSeh146360 			break;
693bb5e3b2fSeh146360 	}
694bb5e3b2fSeh146360 	mutex_exit(&sc->sc_mflock);
695bb5e3b2fSeh146360 
696bb5e3b2fSeh146360 	/*
697bb5e3b2fSeh146360 	 * Unregister from the MAC layer subsystem
698bb5e3b2fSeh146360 	 */
69942516a0cSxinghua wen - Sun Microsystems - Beijing China 	(void) mac_unregister(sc->sc_ic.ic_mach);
700bb5e3b2fSeh146360 
701bb5e3b2fSeh146360 	ddi_remove_intr(dip, IPW2200_PCI_INTR_NUM, sc->sc_iblk);
702bb5e3b2fSeh146360 
703bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_ilock);
704bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_cmd_lock);
705bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_tx_lock);
706bb5e3b2fSeh146360 	mutex_destroy(&sc->sc_mflock);
707924f3e72Seh146360 	mutex_destroy(&sc->sc_resched_lock);
708bb5e3b2fSeh146360 	cv_destroy(&sc->sc_fw_cond);
709bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_status_cond);
710bb5e3b2fSeh146360 	cv_destroy(&sc->sc_cmd_cond);
711bb5e3b2fSeh146360 	cv_destroy(&sc->sc_mfthread_cv);
712bb5e3b2fSeh146360 
713bb5e3b2fSeh146360 	/*
714bb5e3b2fSeh146360 	 * Detach ieee80211
715bb5e3b2fSeh146360 	 */
716bb5e3b2fSeh146360 	ieee80211_detach(&sc->sc_ic);
717bb5e3b2fSeh146360 
718bb5e3b2fSeh146360 	(void) ipw2200_free_firmware(sc);
719bb5e3b2fSeh146360 	ipw2200_ring_free(sc);
720bb5e3b2fSeh146360 
721bb5e3b2fSeh146360 	ddi_regs_map_free(&sc->sc_ioh);
722bb5e3b2fSeh146360 	ddi_remove_minor_node(dip, NULL);
723bb5e3b2fSeh146360 	ddi_soft_state_free(ipw2200_ssp, ddi_get_instance(dip));
724bb5e3b2fSeh146360 
725bb5e3b2fSeh146360 	return (DDI_SUCCESS);
726bb5e3b2fSeh146360 }
727bb5e3b2fSeh146360 
72814d91224Sfei feng - Sun Microsystems - Beijing China /*
72914d91224Sfei feng - Sun Microsystems - Beijing China  * quiesce(9E) entry point.
73014d91224Sfei feng - Sun Microsystems - Beijing China  * This function is called when the system is single-threaded at high
73114d91224Sfei feng - Sun Microsystems - Beijing China  * PIL with preemption disabled. Therefore, this function must not be
73214d91224Sfei feng - Sun Microsystems - Beijing China  * blocked.
73314d91224Sfei feng - Sun Microsystems - Beijing China  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
73414d91224Sfei feng - Sun Microsystems - Beijing China  * DDI_FAILURE indicates an error condition and should almost never happen.
73514d91224Sfei feng - Sun Microsystems - Beijing China  */
73614d91224Sfei feng - Sun Microsystems - Beijing China static int
ipw2200_quiesce(dev_info_t * dip)73714d91224Sfei feng - Sun Microsystems - Beijing China ipw2200_quiesce(dev_info_t *dip)
73876939ce0Seh146360 {
73976939ce0Seh146360 	struct ipw2200_softc	*sc =
74076939ce0Seh146360 	    ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip));
74114d91224Sfei feng - Sun Microsystems - Beijing China 	if (sc == NULL)
74214d91224Sfei feng - Sun Microsystems - Beijing China 		return (DDI_FAILURE);
74314d91224Sfei feng - Sun Microsystems - Beijing China 
744faceed93Sfei feng - Sun Microsystems - Beijing China 	/* by pass any messages, if it's quiesce */
745faceed93Sfei feng - Sun Microsystems - Beijing China 	ipw2200_debug = 0;
746faceed93Sfei feng - Sun Microsystems - Beijing China 
74714d91224Sfei feng - Sun Microsystems - Beijing China 	/*
74814d91224Sfei feng - Sun Microsystems - Beijing China 	 * No more blocking is allowed while we are in the
74914d91224Sfei feng - Sun Microsystems - Beijing China 	 * quiesce(9E) entry point.
75014d91224Sfei feng - Sun Microsystems - Beijing China 	 */
75114d91224Sfei feng - Sun Microsystems - Beijing China 	sc->sc_flags |= IPW2200_FLAG_QUIESCED;
75214d91224Sfei feng - Sun Microsystems - Beijing China 
75314d91224Sfei feng - Sun Microsystems - Beijing China 	/*
75414d91224Sfei feng - Sun Microsystems - Beijing China 	 * Disable and mask all interrupts.
75514d91224Sfei feng - Sun Microsystems - Beijing China 	 */
756faceed93Sfei feng - Sun Microsystems - Beijing China 	ipw2200_master_stop(sc);
757faceed93Sfei feng - Sun Microsystems - Beijing China 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET);
75876939ce0Seh146360 	return (DDI_SUCCESS);
75976939ce0Seh146360 }
76076939ce0Seh146360 
761bb5e3b2fSeh146360 static void
ipw2200_stop(struct ipw2200_softc * sc)762bb5e3b2fSeh146360 ipw2200_stop(struct ipw2200_softc *sc)
763bb5e3b2fSeh146360 {
764bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
765bb5e3b2fSeh146360 
766bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
767bb5e3b2fSeh146360 	    "ipw2200_stop(): enter\n"));
768bb5e3b2fSeh146360 
769bb5e3b2fSeh146360 	ipw2200_master_stop(sc);
770bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET);
771bb5e3b2fSeh146360 
772bb5e3b2fSeh146360 	/*
773bb5e3b2fSeh146360 	 * Reset ring
774bb5e3b2fSeh146360 	 */
775bb5e3b2fSeh146360 	ipw2200_ring_reset(sc);
776bb5e3b2fSeh146360 
777bb5e3b2fSeh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
778bb5e3b2fSeh146360 	sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
779924f3e72Seh146360 	sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED;
780bb5e3b2fSeh146360 
781bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT,
782bb5e3b2fSeh146360 	    "ipw2200_stop(): exit\n"));
783bb5e3b2fSeh146360 }
784bb5e3b2fSeh146360 
785bb5e3b2fSeh146360 static int
ipw2200_config(struct ipw2200_softc * sc)786bb5e3b2fSeh146360 ipw2200_config(struct ipw2200_softc *sc)
787bb5e3b2fSeh146360 {
788bb5e3b2fSeh146360 	struct ieee80211com		*ic = &sc->sc_ic;
789bb5e3b2fSeh146360 	struct ipw2200_configuration	cfg;
790bb5e3b2fSeh146360 	uint32_t			data;
791bb5e3b2fSeh146360 	struct ipw2200_txpower		pwr;
792bb5e3b2fSeh146360 	struct ipw2200_rateset		rs;
793bb5e3b2fSeh146360 	struct ipw2200_wep_key		wkey;
794bb5e3b2fSeh146360 	int				err, i;
795bb5e3b2fSeh146360 
796bb5e3b2fSeh146360 	/*
797bb5e3b2fSeh146360 	 * Set the IBSS mode channel: Tx power
798bb5e3b2fSeh146360 	 */
799bb5e3b2fSeh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
800bb5e3b2fSeh146360 		pwr.mode  = IPW2200_MODE_11B;
801bb5e3b2fSeh146360 		pwr.nchan = 11;
802bb5e3b2fSeh146360 		for (i = 0; i < pwr.nchan; i++) {
803bb5e3b2fSeh146360 			pwr.chan[i].chan  = i + 1;
804bb5e3b2fSeh146360 			pwr.chan[i].power = IPW2200_TXPOWER_MAX;
805bb5e3b2fSeh146360 		}
806bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
807bb5e3b2fSeh146360 		    "ipw2200_config(): Setting .11b channels Tx power\n"));
808bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
809bb5e3b2fSeh146360 		    &pwr, sizeof (pwr), 0);
810bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
811bb5e3b2fSeh146360 			return (err);
812bb5e3b2fSeh146360 
813bb5e3b2fSeh146360 		pwr.mode  = IPW2200_MODE_11G;
814bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
815bb5e3b2fSeh146360 		    "ipw2200_config(): Setting .11g channels Tx power\n"));
816bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER,
817bb5e3b2fSeh146360 		    &pwr, sizeof (pwr), 0);
818bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
819bb5e3b2fSeh146360 			return (err);
820bb5e3b2fSeh146360 	}
821bb5e3b2fSeh146360 
822bb5e3b2fSeh146360 	/*
823bb5e3b2fSeh146360 	 * Set MAC address
824bb5e3b2fSeh146360 	 */
825bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
826bb5e3b2fSeh146360 	    "ipw2200_config(): Setting MAC address to "
827bb5e3b2fSeh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
828bb5e3b2fSeh146360 	    ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
829bb5e3b2fSeh146360 	    ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]));
830bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr,
831bb5e3b2fSeh146360 	    IEEE80211_ADDR_LEN, 0);
832bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
833bb5e3b2fSeh146360 		return (err);
834bb5e3b2fSeh146360 
835bb5e3b2fSeh146360 	/*
836bb5e3b2fSeh146360 	 * Set basic system config settings: configuration(capabilities)
837bb5e3b2fSeh146360 	 */
838bb5e3b2fSeh146360 	(void) memset(&cfg, 0, sizeof (cfg));
839bb5e3b2fSeh146360 	cfg.bluetooth_coexistence	 = 1;
840bb5e3b2fSeh146360 	cfg.multicast_enabled		 = 1;
841bb5e3b2fSeh146360 	cfg.answer_pbreq		 = 1;
842bb5e3b2fSeh146360 	cfg.noise_reported		 = 1;
843924f3e72Seh146360 	cfg.disable_multicast_decryption = 1; /* WPA */
844924f3e72Seh146360 	cfg.disable_unicast_decryption   = 1; /* WPA */
845bb5e3b2fSeh146360 
846bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
847bb5e3b2fSeh146360 	    "ipw2200_config(): Configuring adapter\n"));
848bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
849bb5e3b2fSeh146360 	    &cfg, sizeof (cfg), 0);
850bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
851bb5e3b2fSeh146360 		return (err);
852bb5e3b2fSeh146360 
853bb5e3b2fSeh146360 	/*
854bb5e3b2fSeh146360 	 * Set power mode
855bb5e3b2fSeh146360 	 */
856bb5e3b2fSeh146360 	data = LE_32(IPW2200_POWER_MODE_CAM);
857bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
858bb5e3b2fSeh146360 	    "ipw2200_config(): Setting power mode to %u\n", LE_32(data)));
859bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE,
860bb5e3b2fSeh146360 	    &data, sizeof (data), 0);
861bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
862bb5e3b2fSeh146360 		return (err);
863bb5e3b2fSeh146360 
864bb5e3b2fSeh146360 	/*
865bb5e3b2fSeh146360 	 * Set supported rates
866bb5e3b2fSeh146360 	 */
867bb5e3b2fSeh146360 	rs.mode = IPW2200_MODE_11G;
868bb5e3b2fSeh146360 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
869bb5e3b2fSeh146360 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates;
870bb5e3b2fSeh146360 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates,
871bb5e3b2fSeh146360 	    rs.nrates);
872bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
873bb5e3b2fSeh146360 	    "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates));
874bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
875bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
876bb5e3b2fSeh146360 		return (err);
877bb5e3b2fSeh146360 
878bb5e3b2fSeh146360 	rs.mode = IPW2200_MODE_11A;
879bb5e3b2fSeh146360 	rs.type = IPW2200_RATESET_TYPE_SUPPORTED;
880bb5e3b2fSeh146360 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates;
881bb5e3b2fSeh146360 	(void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates,
882bb5e3b2fSeh146360 	    rs.nrates);
883bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
884bb5e3b2fSeh146360 	    "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates));
885bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0);
886bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
887bb5e3b2fSeh146360 		return (err);
888bb5e3b2fSeh146360 
889bb5e3b2fSeh146360 	/*
890bb5e3b2fSeh146360 	 * Set RTS(request-to-send) threshold
891bb5e3b2fSeh146360 	 */
892bb5e3b2fSeh146360 	data = LE_32(ic->ic_rtsthreshold);
893bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
894bb5e3b2fSeh146360 	    "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data)));
895bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data,
896bb5e3b2fSeh146360 	    sizeof (data), 0);
897bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
898bb5e3b2fSeh146360 		return (err);
899bb5e3b2fSeh146360 
900bb5e3b2fSeh146360 	/*
901bb5e3b2fSeh146360 	 * Set fragmentation threshold
902bb5e3b2fSeh146360 	 */
903bb5e3b2fSeh146360 	data = LE_32(ic->ic_fragthreshold);
904bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
905bb5e3b2fSeh146360 	    "ipw2200_config(): Setting fragmentation threshold to %u\n",
906bb5e3b2fSeh146360 	    LE_32(data)));
907bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data,
908bb5e3b2fSeh146360 	    sizeof (data), 0);
909bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
910bb5e3b2fSeh146360 		return (err);
911bb5e3b2fSeh146360 
912bb5e3b2fSeh146360 	/*
913bb5e3b2fSeh146360 	 * Set desired ESSID if we have
914bb5e3b2fSeh146360 	 */
915bb5e3b2fSeh146360 	if (ic->ic_des_esslen != 0) {
916bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
917bb5e3b2fSeh146360 		    "ipw2200_config(): Setting desired ESSID to "
918bb5e3b2fSeh146360 		    "(%u),%c%c%c%c%c%c%c%c\n",
919bb5e3b2fSeh146360 		    ic->ic_des_esslen,
920bb5e3b2fSeh146360 		    ic->ic_des_essid[0], ic->ic_des_essid[1],
921bb5e3b2fSeh146360 		    ic->ic_des_essid[2], ic->ic_des_essid[3],
922bb5e3b2fSeh146360 		    ic->ic_des_essid[4], ic->ic_des_essid[5],
923bb5e3b2fSeh146360 		    ic->ic_des_essid[6], ic->ic_des_essid[7]));
924bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid,
925bb5e3b2fSeh146360 		    ic->ic_des_esslen, 0);
926bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
927bb5e3b2fSeh146360 			return (err);
928bb5e3b2fSeh146360 	}
929bb5e3b2fSeh146360 
930bb5e3b2fSeh146360 	/*
931bb5e3b2fSeh146360 	 * Set WEP initial vector(random seed)
932bb5e3b2fSeh146360 	 */
933bb5e3b2fSeh146360 	(void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data));
934bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
935bb5e3b2fSeh146360 	    "ipw2200_config(): Setting initialization vector to %u\n",
936bb5e3b2fSeh146360 	    LE_32(data)));
937bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0);
938bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
939bb5e3b2fSeh146360 		return (err);
940bb5e3b2fSeh146360 
941bb5e3b2fSeh146360 	/*
942bb5e3b2fSeh146360 	 * Set WEP if any
943bb5e3b2fSeh146360 	 */
944bb5e3b2fSeh146360 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
945bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
946bb5e3b2fSeh146360 		    "ipw2200_config(): Setting Wep Key\n", LE_32(data)));
947bb5e3b2fSeh146360 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
948bb5e3b2fSeh146360 			wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY;
949bb5e3b2fSeh146360 			wkey.idx = (uint8_t)i;
950bb5e3b2fSeh146360 			wkey.len = ic->ic_nw_keys[i].wk_keylen;
951bb5e3b2fSeh146360 			(void) memset(wkey.key, 0, sizeof (wkey.key));
952bb5e3b2fSeh146360 			if (ic->ic_nw_keys[i].wk_keylen)
953bb5e3b2fSeh146360 				(void) memcpy(wkey.key,
954bb5e3b2fSeh146360 				    ic->ic_nw_keys[i].wk_key,
955bb5e3b2fSeh146360 				    ic->ic_nw_keys[i].wk_keylen);
956bb5e3b2fSeh146360 			err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY,
957bb5e3b2fSeh146360 			    &wkey, sizeof (wkey), 0);
958bb5e3b2fSeh146360 			if (err != DDI_SUCCESS)
959bb5e3b2fSeh146360 				return (err);
960bb5e3b2fSeh146360 		}
961bb5e3b2fSeh146360 	}
962bb5e3b2fSeh146360 
963bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
964bb5e3b2fSeh146360 	    "ipw2200_config(): Enabling adapter\n"));
965bb5e3b2fSeh146360 
966bb5e3b2fSeh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0));
967bb5e3b2fSeh146360 }
968bb5e3b2fSeh146360 
969bb5e3b2fSeh146360 static int
ipw2200_cmd(struct ipw2200_softc * sc,uint32_t type,void * buf,size_t len,int async)970bb5e3b2fSeh146360 ipw2200_cmd(struct ipw2200_softc *sc,
971bb5e3b2fSeh146360 	uint32_t type, void *buf, size_t len, int async)
972bb5e3b2fSeh146360 {
973bb5e3b2fSeh146360 	struct		ipw2200_cmd_desc *cmd;
974bb5e3b2fSeh146360 	clock_t		clk;
975bb5e3b2fSeh146360 	uint32_t	idx;
976bb5e3b2fSeh146360 
977bb5e3b2fSeh146360 	mutex_enter(&sc->sc_cmd_lock);
978bb5e3b2fSeh146360 	while (sc->sc_cmd_free < 1)
979bb5e3b2fSeh146360 		cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock);
980bb5e3b2fSeh146360 
981bb5e3b2fSeh146360 	idx = sc->sc_cmd_cur;
982bb5e3b2fSeh146360 	cmd = &sc->sc_cmdsc[idx];
983bb5e3b2fSeh146360 	(void) memset(cmd, 0, sizeof (*cmd));
984bb5e3b2fSeh146360 
985bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
986bb5e3b2fSeh146360 	    "ipw2200_cmd(): cmd-cur=%d\n", idx));
987bb5e3b2fSeh146360 
988bb5e3b2fSeh146360 	cmd->hdr.type   = IPW2200_HDR_TYPE_COMMAND;
989bb5e3b2fSeh146360 	cmd->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
990bb5e3b2fSeh146360 	cmd->type	= (uint8_t)type;
991bb5e3b2fSeh146360 	if (len == 0 || buf == NULL)
992bb5e3b2fSeh146360 		cmd->len  = 0;
993bb5e3b2fSeh146360 	else {
994bb5e3b2fSeh146360 		cmd->len  = (uint8_t)len;
995bb5e3b2fSeh146360 		(void) memcpy(cmd->data, buf, len);
996bb5e3b2fSeh146360 	}
997bb5e3b2fSeh146360 	sc->sc_done[idx] = 0;
998bb5e3b2fSeh146360 
999bb5e3b2fSeh146360 	/*
1000bb5e3b2fSeh146360 	 * DMA sync
1001bb5e3b2fSeh146360 	 */
1002bb5e3b2fSeh146360 	(void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd,
1003bb5e3b2fSeh146360 	    idx * sizeof (struct ipw2200_cmd_desc),
1004bb5e3b2fSeh146360 	    sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV);
1005bb5e3b2fSeh146360 
1006bb5e3b2fSeh146360 	sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE);
1007bb5e3b2fSeh146360 	sc->sc_cmd_free--;
1008bb5e3b2fSeh146360 
1009bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
1010bb5e3b2fSeh146360 
1011bb5e3b2fSeh146360 	mutex_exit(&sc->sc_cmd_lock);
1012bb5e3b2fSeh146360 
1013bb5e3b2fSeh146360 	if (async)
1014bb5e3b2fSeh146360 		goto out;
1015bb5e3b2fSeh146360 
1016bb5e3b2fSeh146360 	/*
1017bb5e3b2fSeh146360 	 * Wait for command done
1018bb5e3b2fSeh146360 	 */
1019d3d50737SRafael Vanoni 	clk = drv_usectohz(5000000);
1020bb5e3b2fSeh146360 	mutex_enter(&sc->sc_ilock);
1021bb5e3b2fSeh146360 	while (sc->sc_done[idx] == 0) {
1022bb5e3b2fSeh146360 		/* pending */
1023d3d50737SRafael Vanoni 		if (cv_reltimedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock,
1024d3d50737SRafael Vanoni 		    clk, TR_CLOCK_TICK) < 0)
1025bb5e3b2fSeh146360 			break;
1026bb5e3b2fSeh146360 	}
1027bb5e3b2fSeh146360 	mutex_exit(&sc->sc_ilock);
1028bb5e3b2fSeh146360 
1029bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
1030bb5e3b2fSeh146360 	    "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no"));
1031bb5e3b2fSeh146360 
1032bb5e3b2fSeh146360 	if (sc->sc_done[idx] == 0)
1033bb5e3b2fSeh146360 		return (DDI_FAILURE);
1034bb5e3b2fSeh146360 
1035bb5e3b2fSeh146360 out:
1036bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1037bb5e3b2fSeh146360 }
1038bb5e3b2fSeh146360 
1039bb5e3b2fSeh146360 /*
1040bb5e3b2fSeh146360  * If init failed, it will call stop internally. Therefore, it's unnecessary
1041bb5e3b2fSeh146360  * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may
1042bb5e3b2fSeh146360  * be called twice.
1043bb5e3b2fSeh146360  */
1044bb5e3b2fSeh146360 int
ipw2200_init(struct ipw2200_softc * sc)1045bb5e3b2fSeh146360 ipw2200_init(struct ipw2200_softc *sc)
1046bb5e3b2fSeh146360 {
1047bb5e3b2fSeh146360 	int	err;
1048bb5e3b2fSeh146360 
1049bb5e3b2fSeh146360 	/*
1050bb5e3b2fSeh146360 	 * No firmware is available, failed
1051bb5e3b2fSeh146360 	 */
1052bb5e3b2fSeh146360 	if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) {
1053bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1054bb5e3b2fSeh146360 		    "ipw2200_init(): no firmware is available\n"));
1055bb5e3b2fSeh146360 		return (DDI_FAILURE); /* return directly */
1056bb5e3b2fSeh146360 	}
1057bb5e3b2fSeh146360 
1058bb5e3b2fSeh146360 	ipw2200_stop(sc);
1059bb5e3b2fSeh146360 
1060bb5e3b2fSeh146360 	err = ipw2200_chip_reset(sc);
1061bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1062bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1063bb5e3b2fSeh146360 		    "ipw2200_init(): could not reset adapter\n"));
1064bb5e3b2fSeh146360 		goto fail;
1065bb5e3b2fSeh146360 	}
1066bb5e3b2fSeh146360 
1067bb5e3b2fSeh146360 	/*
1068bb5e3b2fSeh146360 	 * Load boot code
1069bb5e3b2fSeh146360 	 */
1070bb5e3b2fSeh146360 	err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size);
1071bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1072bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1073bb5e3b2fSeh146360 		    "ipw2200_init(): could not load boot code\n"));
1074bb5e3b2fSeh146360 		goto fail;
1075bb5e3b2fSeh146360 	}
1076bb5e3b2fSeh146360 
1077bb5e3b2fSeh146360 	/*
1078bb5e3b2fSeh146360 	 * Load boot microcode
1079bb5e3b2fSeh146360 	 */
1080bb5e3b2fSeh146360 	err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size);
1081bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1082bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1083bb5e3b2fSeh146360 		    "ipw2200_init(): could not load microcode\n"));
1084bb5e3b2fSeh146360 		goto fail;
1085bb5e3b2fSeh146360 	}
1086bb5e3b2fSeh146360 
1087bb5e3b2fSeh146360 	ipw2200_master_stop(sc);
1088bb5e3b2fSeh146360 	ipw2200_ring_hwsetup(sc);
1089bb5e3b2fSeh146360 
1090bb5e3b2fSeh146360 	/*
1091bb5e3b2fSeh146360 	 * Load firmware
1092bb5e3b2fSeh146360 	 */
1093bb5e3b2fSeh146360 	err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size);
1094bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1095bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1096bb5e3b2fSeh146360 		    "ipw2200_init(): could not load firmware\n"));
1097bb5e3b2fSeh146360 		goto fail;
1098bb5e3b2fSeh146360 	}
1099bb5e3b2fSeh146360 
1100bb5e3b2fSeh146360 	sc->sc_flags |= IPW2200_FLAG_FW_INITED;
1101bb5e3b2fSeh146360 
1102bb5e3b2fSeh146360 	/*
1103bb5e3b2fSeh146360 	 * Hardware will be enabled after configuration
1104bb5e3b2fSeh146360 	 */
1105bb5e3b2fSeh146360 	err = ipw2200_config(sc);
1106bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1107bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1108bb5e3b2fSeh146360 		    "ipw2200_init(): device configuration failed\n"));
1109bb5e3b2fSeh146360 		goto fail;
1110bb5e3b2fSeh146360 	}
1111bb5e3b2fSeh146360 
1112bb5e3b2fSeh146360 	/*
1113bb5e3b2fSeh146360 	 * workround to prevent too many h/w error.
1114bb5e3b2fSeh146360 	 * delay for a while till h/w is stable.
1115bb5e3b2fSeh146360 	 */
1116bb5e3b2fSeh146360 	delay(drv_usectohz(delay_config_stable));
1117bb5e3b2fSeh146360 
1118bb5e3b2fSeh146360 	return (DDI_SUCCESS); /* return successfully */
1119bb5e3b2fSeh146360 fail:
1120bb5e3b2fSeh146360 	ipw2200_stop(sc);
1121bb5e3b2fSeh146360 	return (err);
1122bb5e3b2fSeh146360 }
1123bb5e3b2fSeh146360 
1124bb5e3b2fSeh146360 /*
1125bb5e3b2fSeh146360  * get hardware configurations from EEPROM embedded within PRO/2200
1126bb5e3b2fSeh146360  */
1127bb5e3b2fSeh146360 static void
ipw2200_hwconf_get(struct ipw2200_softc * sc)1128bb5e3b2fSeh146360 ipw2200_hwconf_get(struct ipw2200_softc *sc)
1129bb5e3b2fSeh146360 {
1130bb5e3b2fSeh146360 	int		i;
1131bb5e3b2fSeh146360 	uint16_t	val;
1132bb5e3b2fSeh146360 
1133bb5e3b2fSeh146360 	/*
1134bb5e3b2fSeh146360 	 * Get mac address
1135bb5e3b2fSeh146360 	 */
1136bb5e3b2fSeh146360 	i = 0;
1137bb5e3b2fSeh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0);
1138bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val >> 8;
1139bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val & 0xff;
1140bb5e3b2fSeh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1);
1141bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val >> 8;
1142bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val & 0xff;
1143bb5e3b2fSeh146360 	val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2);
1144bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val >> 8;
1145bb5e3b2fSeh146360 	sc->sc_macaddr[i++] = val & 0xff;
1146bb5e3b2fSeh146360 
1147bb5e3b2fSeh146360 	/*
1148bb5e3b2fSeh146360 	 * formatted MAC address string
1149bb5e3b2fSeh146360 	 */
1150bb5e3b2fSeh146360 	(void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr),
1151bb5e3b2fSeh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x",
1152bb5e3b2fSeh146360 	    sc->sc_macaddr[0], sc->sc_macaddr[1],
1153bb5e3b2fSeh146360 	    sc->sc_macaddr[2], sc->sc_macaddr[3],
1154bb5e3b2fSeh146360 	    sc->sc_macaddr[4], sc->sc_macaddr[5]);
1155bb5e3b2fSeh146360 
1156bb5e3b2fSeh146360 }
1157bb5e3b2fSeh146360 
1158bb5e3b2fSeh146360 /*
1159bb5e3b2fSeh146360  * all ipw2200 interrupts will be masked by this routine
1160bb5e3b2fSeh146360  */
1161bb5e3b2fSeh146360 static void
ipw2200_master_stop(struct ipw2200_softc * sc)1162bb5e3b2fSeh146360 ipw2200_master_stop(struct ipw2200_softc *sc)
1163bb5e3b2fSeh146360 {
1164bb5e3b2fSeh146360 	int	ntries;
1165bb5e3b2fSeh146360 
1166bb5e3b2fSeh146360 	/*
1167bb5e3b2fSeh146360 	 * disable interrupts
1168bb5e3b2fSeh146360 	 */
1169bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
1170bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER);
1171bb5e3b2fSeh146360 
1172bb5e3b2fSeh146360 	/*
1173bb5e3b2fSeh146360 	 * wait long enough to ensure hardware stop successfully.
1174bb5e3b2fSeh146360 	 */
1175bb5e3b2fSeh146360 	for (ntries = 0; ntries < 500; ntries++) {
1176bb5e3b2fSeh146360 		if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) &
1177bb5e3b2fSeh146360 		    IPW2200_RST_MASTER_DISABLED)
1178bb5e3b2fSeh146360 			break;
1179bb5e3b2fSeh146360 		/* wait for a while */
1180bb5e3b2fSeh146360 		drv_usecwait(100);
1181bb5e3b2fSeh146360 	}
1182faceed93Sfei feng - Sun Microsystems - Beijing China 	if (ntries == 500)
1183bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1184bb5e3b2fSeh146360 		    "ipw2200_master_stop(): timeout\n"));
1185bb5e3b2fSeh146360 
1186bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST,
1187bb5e3b2fSeh146360 	    IPW2200_RST_PRINCETON_RESET |
1188bb5e3b2fSeh146360 	    ipw2200_csr_get32(sc, IPW2200_CSR_RST));
1189bb5e3b2fSeh146360 
1190bb5e3b2fSeh146360 	sc->sc_flags &= ~IPW2200_FLAG_FW_INITED;
1191bb5e3b2fSeh146360 }
1192bb5e3b2fSeh146360 
1193bb5e3b2fSeh146360 /*
1194bb5e3b2fSeh146360  * all ipw2200 interrupts will be masked by this routine
1195bb5e3b2fSeh146360  */
1196bb5e3b2fSeh146360 static int
ipw2200_chip_reset(struct ipw2200_softc * sc)1197bb5e3b2fSeh146360 ipw2200_chip_reset(struct ipw2200_softc *sc)
1198bb5e3b2fSeh146360 {
1199bb5e3b2fSeh146360 	uint32_t	tmp;
1200bb5e3b2fSeh146360 	int		ntries, i;
1201bb5e3b2fSeh146360 
1202bb5e3b2fSeh146360 	ipw2200_master_stop(sc);
1203bb5e3b2fSeh146360 
1204bb5e3b2fSeh146360 	/*
1205bb5e3b2fSeh146360 	 * Move adapter to DO state
1206bb5e3b2fSeh146360 	 */
1207bb5e3b2fSeh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
1208bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
1209bb5e3b2fSeh146360 
1210bb5e3b2fSeh146360 	/*
1211bb5e3b2fSeh146360 	 * Initialize Phase-Locked Level (PLL)
1212bb5e3b2fSeh146360 	 */
1213bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST);
1214bb5e3b2fSeh146360 
1215bb5e3b2fSeh146360 	/*
1216bb5e3b2fSeh146360 	 * Wait for clock stabilization
1217bb5e3b2fSeh146360 	 */
1218bb5e3b2fSeh146360 	for (ntries = 0; ntries < 1000; ntries++) {
1219bb5e3b2fSeh146360 		if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) &
1220bb5e3b2fSeh146360 		    IPW2200_CTL_CLOCK_READY)
1221bb5e3b2fSeh146360 			break;
1222bb5e3b2fSeh146360 		drv_usecwait(200);
1223bb5e3b2fSeh146360 	}
1224bb5e3b2fSeh146360 	if (ntries == 1000) {
1225bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
1226bb5e3b2fSeh146360 		    "ipw2200_chip_reset(): timeout\n"));
1227bb5e3b2fSeh146360 		return (DDI_FAILURE);
1228bb5e3b2fSeh146360 	}
1229bb5e3b2fSeh146360 
1230bb5e3b2fSeh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST);
1231bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET);
1232bb5e3b2fSeh146360 
1233bb5e3b2fSeh146360 	drv_usecwait(10);
1234bb5e3b2fSeh146360 
1235bb5e3b2fSeh146360 	tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL);
1236bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT);
1237bb5e3b2fSeh146360 
1238bb5e3b2fSeh146360 	/*
1239bb5e3b2fSeh146360 	 * clear NIC memory
1240bb5e3b2fSeh146360 	 */
1241bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0);
1242bb5e3b2fSeh146360 	for (i = 0; i < 0xc000; i++)
1243bb5e3b2fSeh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0);
1244bb5e3b2fSeh146360 
1245bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1246bb5e3b2fSeh146360 }
1247bb5e3b2fSeh146360 
1248bb5e3b2fSeh146360 /*
1249bb5e3b2fSeh146360  * This function is used by wificonfig/dladm to get the current
1250bb5e3b2fSeh146360  * radio status, it is off/on
1251bb5e3b2fSeh146360  */
1252bb5e3b2fSeh146360 int
ipw2200_radio_status(struct ipw2200_softc * sc)1253bb5e3b2fSeh146360 ipw2200_radio_status(struct ipw2200_softc *sc)
1254bb5e3b2fSeh146360 {
1255bb5e3b2fSeh146360 	int	val;
1256bb5e3b2fSeh146360 
1257bb5e3b2fSeh146360 	val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) &
1258bb5e3b2fSeh146360 	    IPW2200_IO_RADIO_ENABLED) ? 1 : 0;
1259bb5e3b2fSeh146360 
1260bb5e3b2fSeh146360 	return (val);
1261bb5e3b2fSeh146360 }
1262bb5e3b2fSeh146360 /*
1263bb5e3b2fSeh146360  * This function is used to get the statistic
1264bb5e3b2fSeh146360  */
1265bb5e3b2fSeh146360 void
ipw2200_get_statistics(struct ipw2200_softc * sc)1266bb5e3b2fSeh146360 ipw2200_get_statistics(struct ipw2200_softc *sc)
1267bb5e3b2fSeh146360 {
1268bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1269bb5e3b2fSeh146360 
1270bb5e3b2fSeh146360 	uint32_t size, buf[128];
1271bb5e3b2fSeh146360 
1272bb5e3b2fSeh146360 	if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) {
1273bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
1274bb5e3b2fSeh146360 		    "ipw2200_get_statistic(): fw doesn't download yet."));
1275bb5e3b2fSeh146360 		return;
1276bb5e3b2fSeh146360 	}
1277bb5e3b2fSeh146360 
1278bb5e3b2fSeh146360 	size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1);
1279bb5e3b2fSeh146360 	ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size);
1280bb5e3b2fSeh146360 
1281bb5e3b2fSeh146360 	/*
1282bb5e3b2fSeh146360 	 * To retrieve the statistic information into proper places. There are
1283bb5e3b2fSeh146360 	 * lot of information. These table will be read once a second.
1284bb5e3b2fSeh146360 	 * Hopefully, it will not effect the performance.
1285bb5e3b2fSeh146360 	 */
1286bb5e3b2fSeh146360 
1287bb5e3b2fSeh146360 	/*
1288bb5e3b2fSeh146360 	 * For the tx/crc information, we can get them from chip directly;
1289bb5e3b2fSeh146360 	 * For the rx/wep error/(rts) related information, leave them net80211.
1290bb5e3b2fSeh146360 	 */
1291bb5e3b2fSeh146360 	/* WIFI_STAT_TX_FRAGS */
1292bb5e3b2fSeh146360 	ic->ic_stats.is_tx_frags = (uint32_t)buf[5];
1293bb5e3b2fSeh146360 	/* WIFI_STAT_MCAST_TX */
1294bb5e3b2fSeh146360 	ic->ic_stats.is_tx_mcast = (uint32_t)buf[31];
1295bb5e3b2fSeh146360 	/* WIFI_STAT_TX_RETRANS */
1296bb5e3b2fSeh146360 	ic->ic_stats.is_tx_retries = (uint32_t)buf[56];
1297bb5e3b2fSeh146360 	/* WIFI_STAT_TX_FAILED */
1298bb5e3b2fSeh146360 	ic->ic_stats.is_tx_failed = (uint32_t)buf[57];
1299bb5e3b2fSeh146360 	/* MAC_STAT_OBYTES */
1300bb5e3b2fSeh146360 	ic->ic_stats.is_tx_bytes = (uint32_t)buf[64];
1301bb5e3b2fSeh146360 }
1302bb5e3b2fSeh146360 
1303bb5e3b2fSeh146360 /*
1304bb5e3b2fSeh146360  * DMA region alloc subroutine
1305bb5e3b2fSeh146360  */
1306bb5e3b2fSeh146360 int
ipw2200_dma_region_alloc(struct ipw2200_softc * sc,struct dma_region * dr,size_t size,uint_t dir,uint_t flags)1307bb5e3b2fSeh146360 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr,
1308bb5e3b2fSeh146360 	size_t size, uint_t dir, uint_t flags)
1309bb5e3b2fSeh146360 {
1310bb5e3b2fSeh146360 	dev_info_t	*dip = sc->sc_dip;
1311bb5e3b2fSeh146360 	int		err;
1312bb5e3b2fSeh146360 
1313bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1314bb5e3b2fSeh146360 	    "ipw2200_dma_region_alloc(): size =%u\n", size));
1315bb5e3b2fSeh146360 
1316bb5e3b2fSeh146360 	err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL,
1317bb5e3b2fSeh146360 	    &dr->dr_hnd);
1318bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1319bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1320bb5e3b2fSeh146360 		    "ipw2200_dma_region_alloc(): "
1321bb5e3b2fSeh146360 		    "ddi_dma_alloc_handle() failed\n"));
1322bb5e3b2fSeh146360 		goto fail0;
1323bb5e3b2fSeh146360 	}
1324bb5e3b2fSeh146360 
1325bb5e3b2fSeh146360 	err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr,
1326bb5e3b2fSeh146360 	    flags, DDI_DMA_SLEEP, NULL,
1327bb5e3b2fSeh146360 	    &dr->dr_base, &dr->dr_size, &dr->dr_acc);
1328bb5e3b2fSeh146360 	if (err != DDI_SUCCESS) {
1329bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1330bb5e3b2fSeh146360 		    "ipw2200_dma_region_alloc(): "
1331bb5e3b2fSeh146360 		    "ddi_dma_mem_alloc() failed\n"));
1332bb5e3b2fSeh146360 		goto fail1;
1333bb5e3b2fSeh146360 	}
1334bb5e3b2fSeh146360 
1335bb5e3b2fSeh146360 	err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL,
1336bb5e3b2fSeh146360 	    dr->dr_base, dr->dr_size,
1337bb5e3b2fSeh146360 	    dir | flags, DDI_DMA_SLEEP, NULL,
1338bb5e3b2fSeh146360 	    &dr->dr_cookie, &dr->dr_ccnt);
1339bb5e3b2fSeh146360 	if (err != DDI_DMA_MAPPED) {
1340bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1341bb5e3b2fSeh146360 		    "ipw2200_dma_region_alloc(): "
1342bb5e3b2fSeh146360 		    "ddi_dma_addr_bind_handle() failed\n"));
1343bb5e3b2fSeh146360 		goto fail2;
1344bb5e3b2fSeh146360 	}
1345bb5e3b2fSeh146360 
1346bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1347bb5e3b2fSeh146360 	    "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt));
1348bb5e3b2fSeh146360 
1349bb5e3b2fSeh146360 	if (dr->dr_ccnt != 1) {
1350bb5e3b2fSeh146360 		err = DDI_FAILURE;
1351bb5e3b2fSeh146360 		goto fail3;
1352bb5e3b2fSeh146360 	}
1353bb5e3b2fSeh146360 
1354bb5e3b2fSeh146360 	dr->dr_pbase = dr->dr_cookie.dmac_address;
1355bb5e3b2fSeh146360 
1356bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT,
1357bb5e3b2fSeh146360 	    "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n",
1358bb5e3b2fSeh146360 	    dr->dr_pbase));
1359bb5e3b2fSeh146360 
1360bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1361bb5e3b2fSeh146360 
1362bb5e3b2fSeh146360 fail3:
1363bb5e3b2fSeh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1364bb5e3b2fSeh146360 fail2:
1365bb5e3b2fSeh146360 	ddi_dma_mem_free(&dr->dr_acc);
1366bb5e3b2fSeh146360 fail1:
1367bb5e3b2fSeh146360 	ddi_dma_free_handle(&dr->dr_hnd);
1368bb5e3b2fSeh146360 fail0:
1369bb5e3b2fSeh146360 	return (err);
1370bb5e3b2fSeh146360 }
1371bb5e3b2fSeh146360 
1372bb5e3b2fSeh146360 void
ipw2200_dma_region_free(struct dma_region * dr)1373bb5e3b2fSeh146360 ipw2200_dma_region_free(struct dma_region *dr)
1374bb5e3b2fSeh146360 {
1375bb5e3b2fSeh146360 	(void) ddi_dma_unbind_handle(dr->dr_hnd);
1376bb5e3b2fSeh146360 	ddi_dma_mem_free(&dr->dr_acc);
1377bb5e3b2fSeh146360 	ddi_dma_free_handle(&dr->dr_hnd);
1378bb5e3b2fSeh146360 }
1379bb5e3b2fSeh146360 
1380bb5e3b2fSeh146360 static int
ipw2200_ring_alloc(struct ipw2200_softc * sc)1381bb5e3b2fSeh146360 ipw2200_ring_alloc(struct ipw2200_softc *sc)
1382bb5e3b2fSeh146360 {
1383bb5e3b2fSeh146360 	int	err, i;
1384bb5e3b2fSeh146360 
1385bb5e3b2fSeh146360 	/*
1386bb5e3b2fSeh146360 	 * tx desc ring
1387bb5e3b2fSeh146360 	 */
1388bb5e3b2fSeh146360 	sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring";
1389bb5e3b2fSeh146360 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc,
1390bb5e3b2fSeh146360 	    IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc),
1391bb5e3b2fSeh146360 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1392bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1393bb5e3b2fSeh146360 		goto fail0;
1394bb5e3b2fSeh146360 	/*
1395bb5e3b2fSeh146360 	 * tx buffer array
1396bb5e3b2fSeh146360 	 */
1397bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++) {
1398bb5e3b2fSeh146360 		sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf";
1399bb5e3b2fSeh146360 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i],
1400bb5e3b2fSeh146360 		    IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING);
1401bb5e3b2fSeh146360 		if (err != DDI_SUCCESS) {
1402bb5e3b2fSeh146360 			while (i >= 0) {
1403bb5e3b2fSeh146360 				ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1404bb5e3b2fSeh146360 				i--;
1405bb5e3b2fSeh146360 			}
1406bb5e3b2fSeh146360 			goto fail1;
1407bb5e3b2fSeh146360 		}
1408bb5e3b2fSeh146360 	}
1409bb5e3b2fSeh146360 	/*
1410bb5e3b2fSeh146360 	 * rx buffer array
1411bb5e3b2fSeh146360 	 */
1412bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++) {
1413bb5e3b2fSeh146360 		sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf";
1414bb5e3b2fSeh146360 		err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i],
1415bb5e3b2fSeh146360 		    IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING);
1416bb5e3b2fSeh146360 		if (err != DDI_SUCCESS) {
1417bb5e3b2fSeh146360 			while (i >= 0) {
1418bb5e3b2fSeh146360 				ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1419bb5e3b2fSeh146360 				i--;
1420bb5e3b2fSeh146360 			}
1421bb5e3b2fSeh146360 			goto fail2;
1422bb5e3b2fSeh146360 		}
1423bb5e3b2fSeh146360 	}
1424bb5e3b2fSeh146360 	/*
1425bb5e3b2fSeh146360 	 * cmd desc ring
1426bb5e3b2fSeh146360 	 */
1427bb5e3b2fSeh146360 	sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring";
1428bb5e3b2fSeh146360 	err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc,
1429bb5e3b2fSeh146360 	    IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc),
1430bb5e3b2fSeh146360 	    DDI_DMA_WRITE, DDI_DMA_CONSISTENT);
1431bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1432bb5e3b2fSeh146360 		goto fail3;
1433bb5e3b2fSeh146360 
1434bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1435bb5e3b2fSeh146360 
1436bb5e3b2fSeh146360 fail3:
1437bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1438bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1439bb5e3b2fSeh146360 fail2:
1440bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1441bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1442bb5e3b2fSeh146360 fail1:
1443bb5e3b2fSeh146360 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
1444bb5e3b2fSeh146360 fail0:
1445bb5e3b2fSeh146360 	return (err);
1446bb5e3b2fSeh146360 }
1447bb5e3b2fSeh146360 
1448bb5e3b2fSeh146360 static void
ipw2200_ring_free(struct ipw2200_softc * sc)1449bb5e3b2fSeh146360 ipw2200_ring_free(struct ipw2200_softc *sc)
1450bb5e3b2fSeh146360 {
1451bb5e3b2fSeh146360 	int	i;
1452bb5e3b2fSeh146360 
1453bb5e3b2fSeh146360 	/*
1454bb5e3b2fSeh146360 	 * tx ring desc
1455bb5e3b2fSeh146360 	 */
1456bb5e3b2fSeh146360 	ipw2200_dma_region_free(&sc->sc_dma_txdsc);
1457bb5e3b2fSeh146360 	/*
1458bb5e3b2fSeh146360 	 * tx buf
1459bb5e3b2fSeh146360 	 */
1460bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1461bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]);
1462bb5e3b2fSeh146360 	/*
1463bb5e3b2fSeh146360 	 * rx buf
1464bb5e3b2fSeh146360 	 */
1465bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1466bb5e3b2fSeh146360 		ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]);
1467bb5e3b2fSeh146360 	/*
1468bb5e3b2fSeh146360 	 * command ring desc
1469bb5e3b2fSeh146360 	 */
1470bb5e3b2fSeh146360 	ipw2200_dma_region_free(&sc->sc_dma_cmdsc);
1471bb5e3b2fSeh146360 }
1472bb5e3b2fSeh146360 
1473bb5e3b2fSeh146360 static void
ipw2200_ring_reset(struct ipw2200_softc * sc)1474bb5e3b2fSeh146360 ipw2200_ring_reset(struct ipw2200_softc *sc)
1475bb5e3b2fSeh146360 {
1476bb5e3b2fSeh146360 	int i;
1477bb5e3b2fSeh146360 
1478bb5e3b2fSeh146360 	/*
1479bb5e3b2fSeh146360 	 * tx desc ring & buffer array
1480bb5e3b2fSeh146360 	 */
1481bb5e3b2fSeh146360 	sc->sc_tx_cur   = 0;
1482bb5e3b2fSeh146360 	sc->sc_tx_free  = IPW2200_TX_RING_SIZE;
1483bb5e3b2fSeh146360 	sc->sc_txdsc    = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base;
1484bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_TX_RING_SIZE; i++)
1485bb5e3b2fSeh146360 		sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base;
1486bb5e3b2fSeh146360 	/*
1487bb5e3b2fSeh146360 	 * rx buffer array
1488bb5e3b2fSeh146360 	 */
1489bb5e3b2fSeh146360 	sc->sc_rx_cur   = 0;
1490bb5e3b2fSeh146360 	sc->sc_rx_free  = IPW2200_RX_RING_SIZE;
1491bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1492bb5e3b2fSeh146360 		sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base;
1493bb5e3b2fSeh146360 
1494bb5e3b2fSeh146360 	/*
1495bb5e3b2fSeh146360 	 * command desc ring
1496bb5e3b2fSeh146360 	 */
1497bb5e3b2fSeh146360 	sc->sc_cmd_cur  = 0;
1498bb5e3b2fSeh146360 	sc->sc_cmd_free = IPW2200_CMD_RING_SIZE;
1499bb5e3b2fSeh146360 	sc->sc_cmdsc    = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base;
1500bb5e3b2fSeh146360 }
1501bb5e3b2fSeh146360 
1502bb5e3b2fSeh146360 /*
1503bb5e3b2fSeh146360  * tx, rx rings and command initialization
1504bb5e3b2fSeh146360  */
1505bb5e3b2fSeh146360 static int
ipw2200_ring_init(struct ipw2200_softc * sc)1506bb5e3b2fSeh146360 ipw2200_ring_init(struct ipw2200_softc *sc)
1507bb5e3b2fSeh146360 {
1508bb5e3b2fSeh146360 	int	err;
1509bb5e3b2fSeh146360 
1510bb5e3b2fSeh146360 	err = ipw2200_ring_alloc(sc);
1511bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1512bb5e3b2fSeh146360 		return (err);
1513bb5e3b2fSeh146360 
1514bb5e3b2fSeh146360 	ipw2200_ring_reset(sc);
1515bb5e3b2fSeh146360 
1516bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1517bb5e3b2fSeh146360 }
1518bb5e3b2fSeh146360 
1519bb5e3b2fSeh146360 static void
ipw2200_ring_hwsetup(struct ipw2200_softc * sc)1520bb5e3b2fSeh146360 ipw2200_ring_hwsetup(struct ipw2200_softc *sc)
1521bb5e3b2fSeh146360 {
1522bb5e3b2fSeh146360 	int	i;
1523bb5e3b2fSeh146360 
1524bb5e3b2fSeh146360 	/*
1525bb5e3b2fSeh146360 	 * command desc ring
1526bb5e3b2fSeh146360 	 */
1527bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase);
1528bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE);
1529bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur);
1530bb5e3b2fSeh146360 
1531bb5e3b2fSeh146360 	/*
1532bb5e3b2fSeh146360 	 * tx desc ring.  only tx1 is used, tx2, tx3, and tx4 are unused
1533bb5e3b2fSeh146360 	 */
1534bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase);
1535bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE);
1536bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
1537bb5e3b2fSeh146360 
1538bb5e3b2fSeh146360 	/*
1539bb5e3b2fSeh146360 	 * tx2, tx3, tx4 is not used
1540bb5e3b2fSeh146360 	 */
1541bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase);
1542bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE);
1543bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0);
1544bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0);
1545bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase);
1546bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE);
1547bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0);
1548bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0);
1549bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase);
1550bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE);
1551bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0);
1552bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0);
1553bb5e3b2fSeh146360 
1554bb5e3b2fSeh146360 	/*
1555bb5e3b2fSeh146360 	 * rx buffer ring
1556bb5e3b2fSeh146360 	 */
1557bb5e3b2fSeh146360 	for (i = 0; i < IPW2200_RX_RING_SIZE; i++)
1558bb5e3b2fSeh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4,
1559bb5e3b2fSeh146360 		    sc->sc_dma_rxbufs[i].dr_pbase);
1560bb5e3b2fSeh146360 	/*
1561bb5e3b2fSeh146360 	 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1
1562bb5e3b2fSeh146360 	 */
1563bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
1564bb5e3b2fSeh146360 	    RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE));
1565bb5e3b2fSeh146360 }
1566bb5e3b2fSeh146360 
1567bb5e3b2fSeh146360 int
ipw2200_start_scan(struct ipw2200_softc * sc)1568bb5e3b2fSeh146360 ipw2200_start_scan(struct ipw2200_softc *sc)
1569bb5e3b2fSeh146360 {
1570bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1571bb5e3b2fSeh146360 	struct ipw2200_scan	scan;
1572bb5e3b2fSeh146360 	uint8_t			*ch;
1573bb5e3b2fSeh146360 	int			cnt, i;
1574bb5e3b2fSeh146360 
1575bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
1576bb5e3b2fSeh146360 	    "ipw2200_start_scan(): start scanning \n"));
1577bb5e3b2fSeh146360 
1578bb5e3b2fSeh146360 	/*
1579bb5e3b2fSeh146360 	 * start scanning
1580bb5e3b2fSeh146360 	 */
1581bb5e3b2fSeh146360 	sc->sc_flags |= IPW2200_FLAG_SCANNING;
1582bb5e3b2fSeh146360 
1583bb5e3b2fSeh146360 	(void) memset(&scan, 0, sizeof (scan));
1584bb5e3b2fSeh146360 	scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED :
1585bb5e3b2fSeh146360 	    IPW2200_SCAN_TYPE_BROADCAST;
1586bb5e3b2fSeh146360 	scan.dwelltime = LE_16(40); /* The interval is set up to 40 */
1587bb5e3b2fSeh146360 
1588bb5e3b2fSeh146360 	/*
1589bb5e3b2fSeh146360 	 * Compact supported channel number(5G) into a single buffer
1590bb5e3b2fSeh146360 	 */
1591bb5e3b2fSeh146360 	ch = scan.channels;
1592bb5e3b2fSeh146360 	cnt = 0;
1593bb5e3b2fSeh146360 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1594bb5e3b2fSeh146360 		if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) &&
1595bb5e3b2fSeh146360 		    isset(ic->ic_chan_active, i)) {
1596bb5e3b2fSeh146360 			*++ch = (uint8_t)i;
1597bb5e3b2fSeh146360 			cnt++;
1598bb5e3b2fSeh146360 		}
1599bb5e3b2fSeh146360 	}
1600bb5e3b2fSeh146360 	*(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt;
16013b608f65Sql147931 	ch = (cnt > 0) ? (ch + 1) : (scan.channels);
1602bb5e3b2fSeh146360 
1603bb5e3b2fSeh146360 	/*
1604bb5e3b2fSeh146360 	 * Compact supported channel number(2G) into a single buffer
1605bb5e3b2fSeh146360 	 */
1606bb5e3b2fSeh146360 	cnt = 0;
1607bb5e3b2fSeh146360 	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
1608bb5e3b2fSeh146360 		if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) &&
1609bb5e3b2fSeh146360 		    isset(ic->ic_chan_active, i)) {
1610bb5e3b2fSeh146360 			*++ch = (uint8_t)i;
1611bb5e3b2fSeh146360 			cnt++;
1612bb5e3b2fSeh146360 		}
1613bb5e3b2fSeh146360 	}
1614bb5e3b2fSeh146360 	*(ch - cnt) = IPW2200_CHAN_2GHZ | cnt;
1615bb5e3b2fSeh146360 
1616bb5e3b2fSeh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1));
1617bb5e3b2fSeh146360 }
1618bb5e3b2fSeh146360 
1619bb5e3b2fSeh146360 int
ipw2200_auth_and_assoc(struct ipw2200_softc * sc)1620bb5e3b2fSeh146360 ipw2200_auth_and_assoc(struct ipw2200_softc *sc)
1621bb5e3b2fSeh146360 {
1622bb5e3b2fSeh146360 	struct ieee80211com		*ic = &sc->sc_ic;
1623bb5e3b2fSeh146360 	struct ieee80211_node		*in = ic->ic_bss;
1624bb5e3b2fSeh146360 	struct ipw2200_configuration	cfg;
1625bb5e3b2fSeh146360 	struct ipw2200_rateset		rs;
1626bb5e3b2fSeh146360 	struct ipw2200_associate	assoc;
1627bb5e3b2fSeh146360 	uint32_t			data;
1628bb5e3b2fSeh146360 	int				err;
1629924f3e72Seh146360 	uint8_t				*wpa_level;
1630924f3e72Seh146360 
1631924f3e72Seh146360 	if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED) {
1632924f3e72Seh146360 		/* already associated */
1633924f3e72Seh146360 		return (-1);
1634924f3e72Seh146360 	}
1635bb5e3b2fSeh146360 
1636bb5e3b2fSeh146360 	/*
1637bb5e3b2fSeh146360 	 * set the confiuration
1638bb5e3b2fSeh146360 	 */
1639bb5e3b2fSeh146360 	if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) {
1640bb5e3b2fSeh146360 		/* enable b/g auto-detection */
1641bb5e3b2fSeh146360 		(void) memset(&cfg, 0, sizeof (cfg));
1642bb5e3b2fSeh146360 		cfg.bluetooth_coexistence = 1;
1643bb5e3b2fSeh146360 		cfg.multicast_enabled	  = 1;
1644bb5e3b2fSeh146360 		cfg.use_protection	  = 1;
1645bb5e3b2fSeh146360 		cfg.answer_pbreq	  = 1;
1646bb5e3b2fSeh146360 		cfg.noise_reported	  = 1;
1647924f3e72Seh146360 		cfg.disable_multicast_decryption = 1; /* WPA */
1648924f3e72Seh146360 		cfg.disable_unicast_decryption   = 1; /* WPA */
1649bb5e3b2fSeh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG,
1650bb5e3b2fSeh146360 		    &cfg, sizeof (cfg), 1);
1651bb5e3b2fSeh146360 		if (err != DDI_SUCCESS)
1652bb5e3b2fSeh146360 			return (err);
1653bb5e3b2fSeh146360 	}
1654bb5e3b2fSeh146360 
1655bb5e3b2fSeh146360 	/*
1656bb5e3b2fSeh146360 	 * set the essid, may be null/hidden AP
1657bb5e3b2fSeh146360 	 */
1658bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1659bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1660bb5e3b2fSeh146360 	    "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n",
1661bb5e3b2fSeh146360 	    in->in_esslen,
1662bb5e3b2fSeh146360 	    in->in_essid[0], in->in_essid[1],
1663bb5e3b2fSeh146360 	    in->in_essid[2], in->in_essid[3],
1664bb5e3b2fSeh146360 	    in->in_essid[4], in->in_essid[5],
1665bb5e3b2fSeh146360 	    in->in_essid[6], in->in_essid[7]));
1666bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid,
1667bb5e3b2fSeh146360 	    in->in_esslen, 1);
1668bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1669bb5e3b2fSeh146360 		return (err);
1670bb5e3b2fSeh146360 
1671bb5e3b2fSeh146360 	/*
1672bb5e3b2fSeh146360 	 * set the rate: the rate set has already been ''negocitated''
1673bb5e3b2fSeh146360 	 */
1674bb5e3b2fSeh146360 	rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
1675bb5e3b2fSeh146360 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
1676bb5e3b2fSeh146360 	rs.type = IPW2200_RATESET_TYPE_NEGOCIATED;
1677bb5e3b2fSeh146360 	rs.nrates = in->in_rates.ir_nrates;
1678bb5e3b2fSeh146360 	(void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates);
1679bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1680bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1681bb5e3b2fSeh146360 	    "setting negotiated rates to(nrates = %u)\n", rs.nrates));
1682bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1);
1683bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1684bb5e3b2fSeh146360 		return (err);
1685bb5e3b2fSeh146360 
1686bb5e3b2fSeh146360 	/*
1687924f3e72Seh146360 	 * invoke command associate
1688924f3e72Seh146360 	 */
1689924f3e72Seh146360 	(void) memset(&assoc, 0, sizeof (assoc));
1690924f3e72Seh146360 
1691924f3e72Seh146360 	/*
1692924f3e72Seh146360 	 * set opt_ie to h/w if associated is WPA, opt_ie has been verified
1693924f3e72Seh146360 	 * by net80211 kernel module.
1694924f3e72Seh146360 	 */
1695924f3e72Seh146360 	if (ic->ic_opt_ie != NULL) {
1696924f3e72Seh146360 
1697924f3e72Seh146360 		wpa_level = (uint8_t *)ic->ic_opt_ie;
1698924f3e72Seh146360 
1699924f3e72Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1700924f3e72Seh146360 		    "ipw2200_auth_and_assoc(): "
1701924f3e72Seh146360 		    "set wpa_ie and wpa_ie_len to h/w. "
1702924f3e72Seh146360 		    "length is %d\n"
1703924f3e72Seh146360 		    "opt_ie[0] = %02X - element vendor\n"
1704924f3e72Seh146360 		    "opt_ie[1] = %02X - length\n"
1705924f3e72Seh146360 		    "opt_ie[2,3,4] = %02X %02X %02X - oui\n"
1706924f3e72Seh146360 		    "opt_ie[5] = %02X - oui type\n"
1707924f3e72Seh146360 		    "opt_ie[6,7] = %02X %02X - spec version \n"
1708924f3e72Seh146360 		    "opt_ie[8,9,10,11] = %02X %02X %02X %02X - gk cipher\n"
1709924f3e72Seh146360 		    "opt_ie[12,13] = %02X %02X - pairwise key cipher(1)\n"
1710924f3e72Seh146360 		    "opt_ie[14,15,16,17] = %02X %02X %02X %02X - ciphers\n"
1711924f3e72Seh146360 		    "opt_ie[18,19] = %02X %02X - authselcont(1) \n"
1712924f3e72Seh146360 		    "opt_ie[20,21,22,23] = %02X %02X %02X %02X - authsels\n",
1713924f3e72Seh146360 		    wpa_level[1], wpa_level[0], wpa_level[1],
1714924f3e72Seh146360 		    wpa_level[2], wpa_level[3], wpa_level[4],
1715924f3e72Seh146360 		    wpa_level[5], wpa_level[6], wpa_level[7],
1716924f3e72Seh146360 		    wpa_level[8], wpa_level[9], wpa_level[10],
1717924f3e72Seh146360 		    wpa_level[11], wpa_level[12], wpa_level[13],
1718924f3e72Seh146360 		    wpa_level[14], wpa_level[15], wpa_level[16],
1719924f3e72Seh146360 		    wpa_level[17], wpa_level[18], wpa_level[19],
1720924f3e72Seh146360 		    wpa_level[20], wpa_level[21], wpa_level[22],
1721924f3e72Seh146360 		    wpa_level[23]));
1722924f3e72Seh146360 
1723924f3e72Seh146360 		err = ipw2200_cmd(sc, IPW2200_CMD_SET_OPTIE,
1724924f3e72Seh146360 		    ic->ic_opt_ie, ic->ic_opt_ie_len, 1);
1725924f3e72Seh146360 		if (err != DDI_SUCCESS)
1726924f3e72Seh146360 			return (err);
1727924f3e72Seh146360 	}
1728924f3e72Seh146360 
1729924f3e72Seh146360 	/*
1730bb5e3b2fSeh146360 	 * set the sensitive
1731bb5e3b2fSeh146360 	 */
1732bb5e3b2fSeh146360 	data = LE_32(in->in_rssi);
1733bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1734bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1735bb5e3b2fSeh146360 	    "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi));
1736bb5e3b2fSeh146360 	err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY,
1737bb5e3b2fSeh146360 	    &data, sizeof (data), 1);
1738bb5e3b2fSeh146360 	if (err != DDI_SUCCESS)
1739bb5e3b2fSeh146360 		return (err);
1740bb5e3b2fSeh146360 
1741bb5e3b2fSeh146360 	/*
1742924f3e72Seh146360 	 * set mode and channel for assocation command
1743bb5e3b2fSeh146360 	 */
1744bb5e3b2fSeh146360 	assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ?
1745bb5e3b2fSeh146360 	    IPW2200_MODE_11A : IPW2200_MODE_11G;
1746bb5e3b2fSeh146360 	assoc.chan = ieee80211_chan2ieee(ic, in->in_chan);
1747924f3e72Seh146360 
1748bb5e3b2fSeh146360 	/*
1749bb5e3b2fSeh146360 	 * use the value set to ic_bss to retraive current sharedmode
1750bb5e3b2fSeh146360 	 */
1751bb5e3b2fSeh146360 	if (ic->ic_bss->in_authmode == WL_SHAREDKEY) {
1752bb5e3b2fSeh146360 		assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED;
1753bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
1754bb5e3b2fSeh146360 		    "ipw2200_auth_and_assoc(): "
1755bb5e3b2fSeh146360 		    "associate to shared key mode, set thru. ioctl"));
1756bb5e3b2fSeh146360 	}
1757924f3e72Seh146360 
1758924f3e72Seh146360 	if (ic->ic_flags & IEEE80211_F_WPA)
1759924f3e72Seh146360 		assoc.policy = LE_16(IPW2200_POLICY_WPA); /* RSN/WPA active */
1760bb5e3b2fSeh146360 	(void) memcpy(assoc.tstamp, in->in_tstamp.data, 8);
1761bb5e3b2fSeh146360 	assoc.capinfo = LE_16(in->in_capinfo);
1762bb5e3b2fSeh146360 	assoc.lintval = LE_16(ic->ic_lintval);
1763bb5e3b2fSeh146360 	assoc.intval  = LE_16(in->in_intval);
1764bb5e3b2fSeh146360 	IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid);
1765bb5e3b2fSeh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS)
1766bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr);
1767bb5e3b2fSeh146360 	else
1768bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid);
1769bb5e3b2fSeh146360 
1770bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1771bb5e3b2fSeh146360 	    "ipw2200_auth_and_assoc(): "
1772bb5e3b2fSeh146360 	    "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), "
1773bb5e3b2fSeh146360 	    "chan(%u), auth(%u)\n",
1774bb5e3b2fSeh146360 	    assoc.bssid[0], assoc.bssid[1], assoc.bssid[2],
1775bb5e3b2fSeh146360 	    assoc.bssid[3], assoc.bssid[4], assoc.bssid[5],
1776bb5e3b2fSeh146360 	    assoc.chan, assoc.auth));
1777bb5e3b2fSeh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE,
1778bb5e3b2fSeh146360 	    &assoc, sizeof (assoc), 1));
1779bb5e3b2fSeh146360 }
1780bb5e3b2fSeh146360 
1781924f3e72Seh146360 /*
1782924f3e72Seh146360  * Send the dis-association command to h/w, will receive notification to claim
1783924f3e72Seh146360  * the connection is dis-associated. So, it's not marked as disassociated this
1784924f3e72Seh146360  * moment.
1785924f3e72Seh146360  */
1786924f3e72Seh146360 static int
ipw2200_disassoc(struct ipw2200_softc * sc)1787924f3e72Seh146360 ipw2200_disassoc(struct ipw2200_softc *sc)
1788924f3e72Seh146360 {
1789924f3e72Seh146360 	struct ipw2200_associate assoc;
1790924f3e72Seh146360 	assoc.type = 2;
1791924f3e72Seh146360 	return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, &assoc,
1792924f3e72Seh146360 	    sizeof (assoc), 1));
1793924f3e72Seh146360 }
1794924f3e72Seh146360 
1795bb5e3b2fSeh146360 /* ARGSUSED */
1796bb5e3b2fSeh146360 static int
ipw2200_newstate(struct ieee80211com * ic,enum ieee80211_state state,int arg)1797bb5e3b2fSeh146360 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg)
1798bb5e3b2fSeh146360 {
1799bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
1800bb5e3b2fSeh146360 	wifi_data_t		wd = { 0 };
1801bb5e3b2fSeh146360 
1802bb5e3b2fSeh146360 	switch (state) {
1803bb5e3b2fSeh146360 	case IEEE80211_S_SCAN:
1804bb5e3b2fSeh146360 		if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) {
1805bb5e3b2fSeh146360 			ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
1806bb5e3b2fSeh146360 			(void) ipw2200_start_scan(sc);
1807bb5e3b2fSeh146360 		}
1808bb5e3b2fSeh146360 		break;
1809bb5e3b2fSeh146360 	case IEEE80211_S_AUTH:
1810924f3e72Seh146360 		/*
1811924f3e72Seh146360 		 * The firmware will fail if we are already associated
1812924f3e72Seh146360 		 */
1813924f3e72Seh146360 		if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED)
1814924f3e72Seh146360 			(void) ipw2200_disassoc(sc);
1815bb5e3b2fSeh146360 		(void) ipw2200_auth_and_assoc(sc);
1816bb5e3b2fSeh146360 		break;
1817bb5e3b2fSeh146360 	case IEEE80211_S_RUN:
1818bb5e3b2fSeh146360 		/*
1819bb5e3b2fSeh146360 		 * We can send data now; update the fastpath with our
1820bb5e3b2fSeh146360 		 * current associated BSSID and other relevant settings.
1821bb5e3b2fSeh146360 		 */
1822924f3e72Seh146360 		wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
1823bb5e3b2fSeh146360 		wd.wd_opmode = ic->ic_opmode;
1824bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
1825bb5e3b2fSeh146360 		(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
1826bb5e3b2fSeh146360 		break;
1827bb5e3b2fSeh146360 	case IEEE80211_S_ASSOC:
1828bb5e3b2fSeh146360 	case IEEE80211_S_INIT:
1829bb5e3b2fSeh146360 		break;
1830bb5e3b2fSeh146360 	}
1831bb5e3b2fSeh146360 
1832bb5e3b2fSeh146360 	/*
1833924f3e72Seh146360 	 * notify to update the link, and WPA
1834bb5e3b2fSeh146360 	 */
1835bb5e3b2fSeh146360 	if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) {
1836924f3e72Seh146360 		ieee80211_notify_node_join(ic, ic->ic_bss);
1837bb5e3b2fSeh146360 	} else if ((ic->ic_state == IEEE80211_S_RUN) &&
1838bb5e3b2fSeh146360 	    (state != IEEE80211_S_RUN)) {
1839924f3e72Seh146360 		ieee80211_notify_node_leave(ic, ic->ic_bss);
1840bb5e3b2fSeh146360 	}
1841bb5e3b2fSeh146360 
1842bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
1843bb5e3b2fSeh146360 	    "ipw2200_newstat(): %s -> %s\n",
1844bb5e3b2fSeh146360 	    ieee80211_state_name[ic->ic_state],
1845bb5e3b2fSeh146360 	    ieee80211_state_name[state]));
1846bb5e3b2fSeh146360 
1847bb5e3b2fSeh146360 	ic->ic_state = state;
1848bb5e3b2fSeh146360 	return (DDI_SUCCESS);
1849bb5e3b2fSeh146360 }
1850bb5e3b2fSeh146360 /*
1851bb5e3b2fSeh146360  * GLD operations
1852bb5e3b2fSeh146360  */
1853bb5e3b2fSeh146360 /* ARGSUSED */
1854bb5e3b2fSeh146360 static int
ipw2200_m_stat(void * arg,uint_t stat,uint64_t * val)1855bb5e3b2fSeh146360 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val)
1856bb5e3b2fSeh146360 {
1857bb5e3b2fSeh146360 	ieee80211com_t		*ic = (ieee80211com_t *)arg;
1858922d2c76Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
1859bb5e3b2fSeh146360 
1860bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
1861bb5e3b2fSeh146360 	    CE_CONT,
1862bb5e3b2fSeh146360 	    "ipw2200_m_stat(): enter\n"));
1863bb5e3b2fSeh146360 	/*
1864bb5e3b2fSeh146360 	 * Some of below statistic data are from hardware, some from net80211
1865bb5e3b2fSeh146360 	 */
1866bb5e3b2fSeh146360 	switch (stat) {
1867922d2c76Seh146360 	case MAC_STAT_NOXMTBUF:
1868922d2c76Seh146360 		*val = ic->ic_stats.is_tx_nobuf;
1869922d2c76Seh146360 		break;
1870922d2c76Seh146360 	case MAC_STAT_IERRORS:
1871922d2c76Seh146360 		*val = sc->sc_stats.sc_rx_len_err;
1872922d2c76Seh146360 		break;
1873922d2c76Seh146360 	case MAC_STAT_OERRORS:
1874922d2c76Seh146360 		*val = sc->sc_stats.sc_tx_discard +
1875922d2c76Seh146360 		    sc->sc_stats.sc_tx_alloc_fail +
1876922d2c76Seh146360 		    sc->sc_stats.sc_tx_encap_fail +
1877922d2c76Seh146360 		    sc->sc_stats.sc_tx_crypto_fail;
1878922d2c76Seh146360 		break;
1879bb5e3b2fSeh146360 	case MAC_STAT_RBYTES:
1880bb5e3b2fSeh146360 		*val = ic->ic_stats.is_rx_bytes;
1881bb5e3b2fSeh146360 		break;
1882bb5e3b2fSeh146360 	case MAC_STAT_IPACKETS:
1883bb5e3b2fSeh146360 		*val = ic->ic_stats.is_rx_frags;
1884bb5e3b2fSeh146360 		break;
1885bb5e3b2fSeh146360 	case MAC_STAT_OBYTES:
1886bb5e3b2fSeh146360 		*val = ic->ic_stats.is_tx_bytes;
1887bb5e3b2fSeh146360 		break;
1888bb5e3b2fSeh146360 	case MAC_STAT_OPACKETS:
1889bb5e3b2fSeh146360 		*val = ic->ic_stats.is_tx_frags;
1890bb5e3b2fSeh146360 		break;
1891bb5e3b2fSeh146360 	/*
1892bb5e3b2fSeh146360 	 * Get below from hardware statistic, retraive net80211 value once 1s
1893bb5e3b2fSeh146360 	 */
1894bb5e3b2fSeh146360 	case WIFI_STAT_TX_FRAGS:
1895bb5e3b2fSeh146360 	case WIFI_STAT_MCAST_TX:
1896bb5e3b2fSeh146360 	case WIFI_STAT_TX_FAILED:
1897bb5e3b2fSeh146360 	case WIFI_STAT_TX_RETRANS:
1898bb5e3b2fSeh146360 	/*
1899bb5e3b2fSeh146360 	 * Get blow information from net80211
1900bb5e3b2fSeh146360 	 */
1901bb5e3b2fSeh146360 	case WIFI_STAT_RTS_SUCCESS:
1902bb5e3b2fSeh146360 	case WIFI_STAT_RTS_FAILURE:
1903bb5e3b2fSeh146360 	case WIFI_STAT_ACK_FAILURE:
1904bb5e3b2fSeh146360 	case WIFI_STAT_RX_FRAGS:
1905bb5e3b2fSeh146360 	case WIFI_STAT_MCAST_RX:
1906bb5e3b2fSeh146360 	case WIFI_STAT_RX_DUPS:
1907bb5e3b2fSeh146360 	case WIFI_STAT_FCS_ERRORS:
1908bb5e3b2fSeh146360 	case WIFI_STAT_WEP_ERRORS:
1909bb5e3b2fSeh146360 		return (ieee80211_stat(ic, stat, val));
1910bb5e3b2fSeh146360 	/*
1911bb5e3b2fSeh146360 	 * Need be supported later
1912bb5e3b2fSeh146360 	 */
1913bb5e3b2fSeh146360 	case MAC_STAT_IFSPEED:
1914bb5e3b2fSeh146360 	default:
1915bb5e3b2fSeh146360 		return (ENOTSUP);
1916bb5e3b2fSeh146360 	}
1917bb5e3b2fSeh146360 	return (0);
1918bb5e3b2fSeh146360 }
1919bb5e3b2fSeh146360 
1920bb5e3b2fSeh146360 /* ARGSUSED */
1921bb5e3b2fSeh146360 static int
ipw2200_m_multicst(void * arg,boolean_t add,const uint8_t * mca)1922bb5e3b2fSeh146360 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
1923bb5e3b2fSeh146360 {
1924bb5e3b2fSeh146360 	/* not supported */
1925bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip,
1926bb5e3b2fSeh146360 	    CE_CONT,
1927bb5e3b2fSeh146360 	    "ipw2200_m_multicst(): enter\n"));
1928bb5e3b2fSeh146360 
1929922d2c76Seh146360 	return (0);
1930bb5e3b2fSeh146360 }
1931bb5e3b2fSeh146360 
1932bb5e3b2fSeh146360 /*
1933bb5e3b2fSeh146360  * Multithread handler for linkstatus, fatal error recovery, get statistic
1934bb5e3b2fSeh146360  */
1935bb5e3b2fSeh146360 static void
ipw2200_thread(struct ipw2200_softc * sc)1936bb5e3b2fSeh146360 ipw2200_thread(struct ipw2200_softc *sc)
1937bb5e3b2fSeh146360 {
1938bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
1939982d85fcSeh146360 	enum ieee80211_state	ostate;
1940bb5e3b2fSeh146360 	int32_t			nlstate;
1941bb5e3b2fSeh146360 	int			stat_cnt = 0;
1942bb5e3b2fSeh146360 
1943bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1944bb5e3b2fSeh146360 	    "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate));
1945bb5e3b2fSeh146360 
1946bb5e3b2fSeh146360 	mutex_enter(&sc->sc_mflock);
1947bb5e3b2fSeh146360 
1948bb5e3b2fSeh146360 	while (sc->sc_mfthread_switch) {
1949bb5e3b2fSeh146360 		/*
1950922d2c76Seh146360 		 * when radio is off or SUSPEND status, nothing to do
1951922d2c76Seh146360 		 */
1952922d2c76Seh146360 		if ((ipw2200_radio_status(sc) == 0) ||
1953922d2c76Seh146360 		    sc->sc_flags & IPW2200_FLAG_SUSPEND) {
1954922d2c76Seh146360 			goto wait_loop;
1955922d2c76Seh146360 		}
1956922d2c76Seh146360 
1957922d2c76Seh146360 		/*
1958bb5e3b2fSeh146360 		 * notify the link state
1959bb5e3b2fSeh146360 		 */
1960bb5e3b2fSeh146360 		if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) {
1961bb5e3b2fSeh146360 
1962bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT,
1963bb5e3b2fSeh146360 			    "ipw2200_thread(): link status --> %d\n",
1964bb5e3b2fSeh146360 			    sc->sc_linkstate));
1965bb5e3b2fSeh146360 
1966bb5e3b2fSeh146360 			sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE;
1967bb5e3b2fSeh146360 			nlstate = sc->sc_linkstate;
1968bb5e3b2fSeh146360 
1969bb5e3b2fSeh146360 			mutex_exit(&sc->sc_mflock);
1970bb5e3b2fSeh146360 			mac_link_update(ic->ic_mach, nlstate);
1971bb5e3b2fSeh146360 			mutex_enter(&sc->sc_mflock);
1972bb5e3b2fSeh146360 		}
1973bb5e3b2fSeh146360 
1974bb5e3b2fSeh146360 		/*
1975bb5e3b2fSeh146360 		 * recovery fatal error
1976bb5e3b2fSeh146360 		 */
1977bb5e3b2fSeh146360 		if (ic->ic_mach &&
1978bb5e3b2fSeh146360 		    (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) {
1979bb5e3b2fSeh146360 
1980bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
1981bb5e3b2fSeh146360 			    "ipw2200_thread(): "
1982bb5e3b2fSeh146360 			    "try to recover fatal hw error\n"));
1983bb5e3b2fSeh146360 
1984bb5e3b2fSeh146360 			sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER;
1985bb5e3b2fSeh146360 			mutex_exit(&sc->sc_mflock);
1986982d85fcSeh146360 
1987924f3e72Seh146360 			/* stop again */
1988982d85fcSeh146360 			ostate = ic->ic_state;
1989bb5e3b2fSeh146360 			(void) ipw2200_init(sc); /* Force state machine */
1990924f3e72Seh146360 
1991bb5e3b2fSeh146360 			/*
1992bb5e3b2fSeh146360 			 * workround. Delay for a while after init especially
1993bb5e3b2fSeh146360 			 * when something wrong happened already.
1994bb5e3b2fSeh146360 			 */
1995bb5e3b2fSeh146360 			delay(drv_usectohz(delay_fatal_recover));
1996982d85fcSeh146360 
1997982d85fcSeh146360 			/*
1998982d85fcSeh146360 			 * Init scan will recovery the original connection if
1999982d85fcSeh146360 			 * the original state is run
2000982d85fcSeh146360 			 */
2001982d85fcSeh146360 			if (ostate != IEEE80211_S_INIT)
2002982d85fcSeh146360 				ieee80211_begin_scan(ic, 0);
2003982d85fcSeh146360 
2004bb5e3b2fSeh146360 			mutex_enter(&sc->sc_mflock);
2005bb5e3b2fSeh146360 		}
2006bb5e3b2fSeh146360 
2007bb5e3b2fSeh146360 		/*
2008bb5e3b2fSeh146360 		 * get statistic, the value will be retrieved by m_stat
2009bb5e3b2fSeh146360 		 */
2010bb5e3b2fSeh146360 		if (stat_cnt == 10) {
2011bb5e3b2fSeh146360 
2012bb5e3b2fSeh146360 			stat_cnt = 0; /* re-start */
2013bb5e3b2fSeh146360 			mutex_exit(&sc->sc_mflock);
2014bb5e3b2fSeh146360 			ipw2200_get_statistics(sc);
2015bb5e3b2fSeh146360 			mutex_enter(&sc->sc_mflock);
2016bb5e3b2fSeh146360 
2017bb5e3b2fSeh146360 		} else
2018bb5e3b2fSeh146360 			stat_cnt++; /* until 1s */
2019bb5e3b2fSeh146360 
2020922d2c76Seh146360 wait_loop:
2021bb5e3b2fSeh146360 		mutex_exit(&sc->sc_mflock);
2022bb5e3b2fSeh146360 		delay(drv_usectohz(delay_aux_thread));
2023bb5e3b2fSeh146360 		mutex_enter(&sc->sc_mflock);
2024bb5e3b2fSeh146360 
2025bb5e3b2fSeh146360 	}
2026bb5e3b2fSeh146360 	sc->sc_mf_thread = NULL;
2027bb5e3b2fSeh146360 	cv_signal(&sc->sc_mfthread_cv);
2028bb5e3b2fSeh146360 	mutex_exit(&sc->sc_mflock);
2029bb5e3b2fSeh146360 }
2030bb5e3b2fSeh146360 
2031bb5e3b2fSeh146360 static int
ipw2200_m_start(void * arg)2032bb5e3b2fSeh146360 ipw2200_m_start(void *arg)
2033bb5e3b2fSeh146360 {
2034bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2035982d85fcSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2036bb5e3b2fSeh146360 
2037bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2038bb5e3b2fSeh146360 	    "ipw2200_m_start(): enter\n"));
2039bb5e3b2fSeh146360 	/*
2040bb5e3b2fSeh146360 	 * initialize ipw2200 hardware, everything ok will start scan
2041bb5e3b2fSeh146360 	 */
2042bb5e3b2fSeh146360 	(void) ipw2200_init(sc);
2043924f3e72Seh146360 
2044982d85fcSeh146360 	/*
2045982d85fcSeh146360 	 * set the state machine to INIT
2046982d85fcSeh146360 	 */
2047982d85fcSeh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2048bb5e3b2fSeh146360 
2049bb5e3b2fSeh146360 	sc->sc_flags |= IPW2200_FLAG_RUNNING;
2050bb5e3b2fSeh146360 
2051924f3e72Seh146360 	/*
2052924f3e72Seh146360 	 * fix KCF bug. - workaround, need to fix it in net80211
2053924f3e72Seh146360 	 */
2054924f3e72Seh146360 	(void) crypto_mech2id(SUN_CKM_RC4);
2055924f3e72Seh146360 
2056922d2c76Seh146360 	return (0);
2057bb5e3b2fSeh146360 }
2058bb5e3b2fSeh146360 
2059bb5e3b2fSeh146360 static void
ipw2200_m_stop(void * arg)2060bb5e3b2fSeh146360 ipw2200_m_stop(void *arg)
2061bb5e3b2fSeh146360 {
2062bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2063982d85fcSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2064bb5e3b2fSeh146360 
2065bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2066bb5e3b2fSeh146360 	    "ipw2200_m_stop(): enter\n"));
2067bb5e3b2fSeh146360 
2068bb5e3b2fSeh146360 	ipw2200_stop(sc);
2069982d85fcSeh146360 	/*
2070982d85fcSeh146360 	 * set the state machine to INIT
2071982d85fcSeh146360 	 */
2072982d85fcSeh146360 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2073bb5e3b2fSeh146360 
2074bb5e3b2fSeh146360 	sc->sc_flags &= ~IPW2200_FLAG_RUNNING;
2075bb5e3b2fSeh146360 }
2076bb5e3b2fSeh146360 
2077bb5e3b2fSeh146360 static int
ipw2200_m_unicst(void * arg,const uint8_t * macaddr)2078bb5e3b2fSeh146360 ipw2200_m_unicst(void *arg, const uint8_t *macaddr)
2079bb5e3b2fSeh146360 {
2080bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2081bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2082bb5e3b2fSeh146360 	int			err;
2083bb5e3b2fSeh146360 
2084bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2085bb5e3b2fSeh146360 	    "ipw2200_m_unicst(): enter\n"));
2086bb5e3b2fSeh146360 
2087bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2088bb5e3b2fSeh146360 	    "ipw2200_m_unicst(): GLD setting MAC address to "
2089bb5e3b2fSeh146360 	    "%02x:%02x:%02x:%02x:%02x:%02x\n",
2090bb5e3b2fSeh146360 	    macaddr[0], macaddr[1], macaddr[2],
2091bb5e3b2fSeh146360 	    macaddr[3], macaddr[4], macaddr[5]));
2092bb5e3b2fSeh146360 
2093bb5e3b2fSeh146360 	if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
2094bb5e3b2fSeh146360 
2095bb5e3b2fSeh146360 		IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
2096bb5e3b2fSeh146360 
2097bb5e3b2fSeh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
2098bb5e3b2fSeh146360 			err = ipw2200_config(sc);
2099bb5e3b2fSeh146360 			if (err != DDI_SUCCESS) {
2100bb5e3b2fSeh146360 				IPW2200_WARN((sc->sc_dip, CE_WARN,
2101bb5e3b2fSeh146360 				    "ipw2200_m_unicst(): "
2102bb5e3b2fSeh146360 				    "device configuration failed\n"));
2103bb5e3b2fSeh146360 				goto fail;
2104bb5e3b2fSeh146360 			}
2105bb5e3b2fSeh146360 		}
2106bb5e3b2fSeh146360 	}
2107922d2c76Seh146360 	return (0);
2108bb5e3b2fSeh146360 fail:
2109922d2c76Seh146360 	return (EIO);
2110bb5e3b2fSeh146360 }
2111bb5e3b2fSeh146360 
2112bb5e3b2fSeh146360 static int
ipw2200_m_promisc(void * arg,boolean_t on)2113bb5e3b2fSeh146360 ipw2200_m_promisc(void *arg, boolean_t on)
2114bb5e3b2fSeh146360 {
2115bb5e3b2fSeh146360 	/* not supported */
2116bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2117bb5e3b2fSeh146360 
2118bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2119bb5e3b2fSeh146360 	    "ipw2200_m_promisc(): enter. "
2120bb5e3b2fSeh146360 	    "GLD setting promiscuous mode - %d\n", on));
2121bb5e3b2fSeh146360 
2122922d2c76Seh146360 	return (0);
2123bb5e3b2fSeh146360 }
2124bb5e3b2fSeh146360 
2125bb5e3b2fSeh146360 static mblk_t *
ipw2200_m_tx(void * arg,mblk_t * mp)2126bb5e3b2fSeh146360 ipw2200_m_tx(void *arg, mblk_t *mp)
2127bb5e3b2fSeh146360 {
2128bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2129bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2130bb5e3b2fSeh146360 	mblk_t			*next;
2131bb5e3b2fSeh146360 
2132bb5e3b2fSeh146360 	/*
2133922d2c76Seh146360 	 * when driver in on suspend state, freemsgchain directly
2134922d2c76Seh146360 	 */
2135922d2c76Seh146360 	if (sc->sc_flags & IPW2200_FLAG_SUSPEND) {
2136922d2c76Seh146360 		IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT,
2137922d2c76Seh146360 		    "ipw2200_m_tx(): suspend status, discard msg\n"));
2138922d2c76Seh146360 		sc->sc_stats.sc_tx_discard++; /* discard data */
2139922d2c76Seh146360 		freemsgchain(mp);
2140922d2c76Seh146360 		return (NULL);
2141922d2c76Seh146360 	}
2142922d2c76Seh146360 
2143922d2c76Seh146360 	/*
2144bb5e3b2fSeh146360 	 * No data frames go out unless we're associated; this
2145bb5e3b2fSeh146360 	 * should not happen as the 802.11 layer does not enable
2146bb5e3b2fSeh146360 	 * the xmit queue until we enter the RUN state.
2147bb5e3b2fSeh146360 	 */
2148bb5e3b2fSeh146360 	if (ic->ic_state != IEEE80211_S_RUN) {
2149bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2150bb5e3b2fSeh146360 		    "ipw2200_m_tx(): discard msg, ic_state = %u\n",
2151bb5e3b2fSeh146360 		    ic->ic_state));
2152922d2c76Seh146360 		sc->sc_stats.sc_tx_discard++; /* discard data */
2153bb5e3b2fSeh146360 		freemsgchain(mp);
2154bb5e3b2fSeh146360 		return (NULL);
2155bb5e3b2fSeh146360 	}
2156bb5e3b2fSeh146360 
2157bb5e3b2fSeh146360 	while (mp != NULL) {
2158bb5e3b2fSeh146360 		next = mp->b_next;
2159bb5e3b2fSeh146360 		mp->b_next = NULL;
2160bb5e3b2fSeh146360 		if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) ==
2161924f3e72Seh146360 		    ENOMEM) {
2162bb5e3b2fSeh146360 			mp->b_next = next;
2163bb5e3b2fSeh146360 			break;
2164bb5e3b2fSeh146360 		}
2165bb5e3b2fSeh146360 		mp = next;
2166bb5e3b2fSeh146360 	}
2167bb5e3b2fSeh146360 	return (mp);
2168bb5e3b2fSeh146360 }
2169bb5e3b2fSeh146360 
2170924f3e72Seh146360 /*
2171924f3e72Seh146360  * ipw2200_send(): send data. softway to handle crypto_encap.
2172924f3e72Seh146360  */
2173bb5e3b2fSeh146360 static int
ipw2200_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)2174bb5e3b2fSeh146360 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
2175bb5e3b2fSeh146360 {
2176bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)ic;
2177bb5e3b2fSeh146360 	struct ieee80211_node	*in;
2178bb5e3b2fSeh146360 	struct ieee80211_frame	*wh;
2179924f3e72Seh146360 	struct ieee80211_key	*k;
2180924f3e72Seh146360 	mblk_t			*m0, *m;
2181bb5e3b2fSeh146360 	size_t			cnt, off;
2182bb5e3b2fSeh146360 	struct ipw2200_tx_desc	*txdsc;
2183bb5e3b2fSeh146360 	struct dma_region	*dr;
2184bb5e3b2fSeh146360 	uint32_t		idx;
2185924f3e72Seh146360 	int			err = DDI_SUCCESS;
2186bb5e3b2fSeh146360 	/* tmp pointer, used to pack header and payload */
2187bb5e3b2fSeh146360 	uint8_t			*p;
2188bb5e3b2fSeh146360 
2189bb5e3b2fSeh146360 	ASSERT(mp->b_next == NULL);
2190bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2191bb5e3b2fSeh146360 	    "ipw2200_send(): enter\n"));
2192bb5e3b2fSeh146360 
2193bb5e3b2fSeh146360 	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) {
2194bb5e3b2fSeh146360 		/*
2195bb5e3b2fSeh146360 		 * skip all management frames since ipw2200 won't generate any
2196bb5e3b2fSeh146360 		 * management frames. Therefore, drop this package.
2197bb5e3b2fSeh146360 		 */
2198bb5e3b2fSeh146360 		freemsg(mp);
2199924f3e72Seh146360 		err = DDI_FAILURE;
2200bb5e3b2fSeh146360 		goto fail0;
2201bb5e3b2fSeh146360 	}
2202bb5e3b2fSeh146360 
2203bb5e3b2fSeh146360 	mutex_enter(&sc->sc_tx_lock);
2204922d2c76Seh146360 	if (sc->sc_flags & IPW2200_FLAG_SUSPEND) {
2205922d2c76Seh146360 		/*
2206922d2c76Seh146360 		 * when sending data, system runs into suspend status,
2207922d2c76Seh146360 		 * return fail directly
2208922d2c76Seh146360 		 */
2209922d2c76Seh146360 		err = ENXIO;
2210922d2c76Seh146360 		goto fail0;
2211922d2c76Seh146360 	}
2212bb5e3b2fSeh146360 
2213bb5e3b2fSeh146360 	/*
2214bb5e3b2fSeh146360 	 * need 1 empty descriptor
2215bb5e3b2fSeh146360 	 */
2216bb5e3b2fSeh146360 	if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) {
2217924f3e72Seh146360 		mutex_enter(&sc->sc_resched_lock);
2218bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN,
2219bb5e3b2fSeh146360 		    "ipw2200_send(): no enough descriptors(%d)\n",
2220bb5e3b2fSeh146360 		    sc->sc_tx_free));
2221bb5e3b2fSeh146360 		ic->ic_stats.is_tx_nobuf++; /* no enough buffer */
2222bb5e3b2fSeh146360 		sc->sc_flags |= IPW2200_FLAG_TX_SCHED;
2223924f3e72Seh146360 		err = ENOMEM;
2224924f3e72Seh146360 		mutex_exit(&sc->sc_resched_lock);
2225bb5e3b2fSeh146360 		goto fail1;
2226bb5e3b2fSeh146360 	}
2227bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
2228bb5e3b2fSeh146360 	    "ipw2200_send():  tx-free=%d,tx-curr=%d\n",
2229bb5e3b2fSeh146360 	    sc->sc_tx_free, sc->sc_tx_cur));
2230bb5e3b2fSeh146360 
2231924f3e72Seh146360 	/*
2232924f3e72Seh146360 	 * put the mp into one blk, and use it to do the crypto_encap
2233924f3e72Seh146360 	 * if necessaary.
2234924f3e72Seh146360 	 */
2235924f3e72Seh146360 	m = allocb(msgdsize(mp) + 32, BPRI_MED);
2236924f3e72Seh146360 	if (m == NULL) { /* can not alloc buf, drop this package */
2237924f3e72Seh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2238924f3e72Seh146360 		    "ipw2200_send(): msg allocation failed\n"));
2239924f3e72Seh146360 		freemsg(mp);
2240922d2c76Seh146360 		sc->sc_stats.sc_tx_alloc_fail++; /* alloc fail */
2241922d2c76Seh146360 		ic->ic_stats.is_tx_failed++;  /* trans failed */
2242924f3e72Seh146360 		err = DDI_FAILURE;
2243924f3e72Seh146360 		goto fail1;
2244924f3e72Seh146360 	}
2245924f3e72Seh146360 	for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
2246924f3e72Seh146360 		cnt = MBLKL(m0);
2247924f3e72Seh146360 		(void) memcpy(m->b_rptr + off, m0->b_rptr, cnt);
2248924f3e72Seh146360 		off += cnt;
2249924f3e72Seh146360 	}
2250924f3e72Seh146360 	m->b_wptr += off;
2251924f3e72Seh146360 
2252924f3e72Seh146360 	/*
2253924f3e72Seh146360 	 * find tx_node, and encapsulate the data
2254924f3e72Seh146360 	 */
2255924f3e72Seh146360 	wh = (struct ieee80211_frame *)m->b_rptr;
2256bb5e3b2fSeh146360 	in = ieee80211_find_txnode(ic, wh->i_addr1);
2257bb5e3b2fSeh146360 	if (in == NULL) { /* can not find the tx node, drop the package */
2258922d2c76Seh146360 		sc->sc_stats.sc_tx_encap_fail++; /* tx encap fail */
2259922d2c76Seh146360 		ic->ic_stats.is_tx_failed++; /* trans failed */
2260bb5e3b2fSeh146360 		freemsg(mp);
2261924f3e72Seh146360 		err = DDI_FAILURE;
2262924f3e72Seh146360 		goto fail2;
2263bb5e3b2fSeh146360 	}
2264bb5e3b2fSeh146360 	in->in_inact = 0;
2265924f3e72Seh146360 
2266924f3e72Seh146360 	(void) ieee80211_encap(ic, m, in);
2267bb5e3b2fSeh146360 	ieee80211_free_node(in);
2268bb5e3b2fSeh146360 
2269924f3e72Seh146360 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
2270924f3e72Seh146360 		k = ieee80211_crypto_encap(ic, m);
2271924f3e72Seh146360 		if (k == NULL) { /* can not get the key, drop packages */
2272924f3e72Seh146360 			IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2273924f3e72Seh146360 			    "ipw2200_send(): "
2274924f3e72Seh146360 			    "Encrypting 802.11 frame failed\n"));
2275922d2c76Seh146360 			sc->sc_stats.sc_tx_crypto_fail++; /* tx encap fail */
2276922d2c76Seh146360 			ic->ic_stats.is_tx_failed++; /* trans failed */
2277924f3e72Seh146360 			freemsg(mp);
2278924f3e72Seh146360 			err = DDI_FAILURE;
2279924f3e72Seh146360 			goto fail2;
2280924f3e72Seh146360 		}
2281924f3e72Seh146360 		wh = (struct ieee80211_frame *)m->b_rptr;
2282924f3e72Seh146360 	}
2283924f3e72Seh146360 
2284bb5e3b2fSeh146360 	/*
2285924f3e72Seh146360 	 * get txdsc
2286bb5e3b2fSeh146360 	 */
2287bb5e3b2fSeh146360 	idx	= sc->sc_tx_cur;
2288bb5e3b2fSeh146360 	txdsc	= &sc->sc_txdsc[idx];
2289bb5e3b2fSeh146360 	(void) memset(txdsc, 0, sizeof (*txdsc));
2290bb5e3b2fSeh146360 	/*
2291bb5e3b2fSeh146360 	 * extract header from message
2292bb5e3b2fSeh146360 	 */
2293bb5e3b2fSeh146360 	p	= (uint8_t *)&txdsc->wh;
2294924f3e72Seh146360 	off	= sizeof (struct ieee80211_frame);
2295924f3e72Seh146360 	(void) memcpy(p, m->b_rptr, off);
2296bb5e3b2fSeh146360 	/*
2297bb5e3b2fSeh146360 	 * extract payload from message
2298bb5e3b2fSeh146360 	 */
2299bb5e3b2fSeh146360 	dr	= &sc->sc_dma_txbufs[idx];
2300bb5e3b2fSeh146360 	p	= sc->sc_txbufs[idx];
2301924f3e72Seh146360 	cnt	= MBLKL(m);
2302924f3e72Seh146360 	(void) memcpy(p, m->b_rptr + off, cnt - off);
2303924f3e72Seh146360 	cnt    -= off;
2304bb5e3b2fSeh146360 
2305bb5e3b2fSeh146360 	txdsc->hdr.type   = IPW2200_HDR_TYPE_DATA;
2306bb5e3b2fSeh146360 	txdsc->hdr.flags  = IPW2200_HDR_FLAG_IRQ;
2307bb5e3b2fSeh146360 	txdsc->cmd	  = IPW2200_DATA_CMD_TX;
2308924f3e72Seh146360 	txdsc->len	  = LE_16(cnt);
2309bb5e3b2fSeh146360 	txdsc->flags	  = 0;
2310bb5e3b2fSeh146360 
2311bb5e3b2fSeh146360 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
2312bb5e3b2fSeh146360 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
2313bb5e3b2fSeh146360 			txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
2314bb5e3b2fSeh146360 	} else if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
2315bb5e3b2fSeh146360 		txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK;
2316bb5e3b2fSeh146360 
2317924f3e72Seh146360 	/* always set it to none wep, because it's handled by software */
2318bb5e3b2fSeh146360 	txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP;
2319bb5e3b2fSeh146360 
2320bb5e3b2fSeh146360 	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
2321bb5e3b2fSeh146360 		txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE;
2322bb5e3b2fSeh146360 
2323bb5e3b2fSeh146360 	txdsc->nseg	    = LE_32(1);
2324bb5e3b2fSeh146360 	txdsc->seg_addr[0]  = LE_32(dr->dr_pbase);
2325924f3e72Seh146360 	txdsc->seg_len[0]   = LE_32(cnt);
2326bb5e3b2fSeh146360 
2327bb5e3b2fSeh146360 	/*
2328bb5e3b2fSeh146360 	 * DMA sync: buffer and desc
2329bb5e3b2fSeh146360 	 */
2330bb5e3b2fSeh146360 	(void) ddi_dma_sync(dr->dr_hnd, 0,
2331bb5e3b2fSeh146360 	    IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV);
2332bb5e3b2fSeh146360 	(void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd,
2333bb5e3b2fSeh146360 	    idx * sizeof (struct ipw2200_tx_desc),
2334bb5e3b2fSeh146360 	    sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV);
2335bb5e3b2fSeh146360 
2336bb5e3b2fSeh146360 	sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE);
2337bb5e3b2fSeh146360 	sc->sc_tx_free--;
2338bb5e3b2fSeh146360 
2339bb5e3b2fSeh146360 	/*
2340bb5e3b2fSeh146360 	 * update txcur
2341bb5e3b2fSeh146360 	 */
2342bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur);
2343bb5e3b2fSeh146360 
2344bb5e3b2fSeh146360 	/*
2345bb5e3b2fSeh146360 	 * success, free the original message
2346bb5e3b2fSeh146360 	 */
2347bb5e3b2fSeh146360 	if (mp)
2348bb5e3b2fSeh146360 		freemsg(mp);
2349924f3e72Seh146360 fail2:
2350924f3e72Seh146360 	if (m)
2351924f3e72Seh146360 		freemsg(m);
2352bb5e3b2fSeh146360 fail1:
2353bb5e3b2fSeh146360 	mutex_exit(&sc->sc_tx_lock);
2354bb5e3b2fSeh146360 fail0:
2355bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2356bb5e3b2fSeh146360 	    "ipw2200_send(): exit - err=%d\n", err));
2357bb5e3b2fSeh146360 
2358bb5e3b2fSeh146360 	return (err);
2359bb5e3b2fSeh146360 }
2360bb5e3b2fSeh146360 
2361bb5e3b2fSeh146360 /*
2362bb5e3b2fSeh146360  * IOCTL handlers
2363bb5e3b2fSeh146360  */
2364bb5e3b2fSeh146360 #define	IEEE80211_IOCTL_REQUIRED	(1)
2365bb5e3b2fSeh146360 #define	IEEE80211_IOCTL_NOT_REQUIRED	(0)
2366bb5e3b2fSeh146360 static void
ipw2200_m_ioctl(void * arg,queue_t * q,mblk_t * m)2367bb5e3b2fSeh146360 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m)
2368bb5e3b2fSeh146360 {
2369bb5e3b2fSeh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2370bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2371bb5e3b2fSeh146360 	uint32_t		err;
2372bb5e3b2fSeh146360 
2373bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT,
2374bb5e3b2fSeh146360 	    "ipw2200_m_ioctl(): enter\n"));
2375bb5e3b2fSeh146360 
2376bb5e3b2fSeh146360 	/*
2377bb5e3b2fSeh146360 	 * Check whether or not need to handle this in net80211
2378bb5e3b2fSeh146360 	 *
2379bb5e3b2fSeh146360 	 */
2380bb5e3b2fSeh146360 	if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED)
2381bb5e3b2fSeh146360 		return;
2382bb5e3b2fSeh146360 
2383bb5e3b2fSeh146360 	err = ieee80211_ioctl(ic, q, m);
2384bb5e3b2fSeh146360 	if (err == ENETRESET) {
2385bb5e3b2fSeh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
2386bb5e3b2fSeh146360 			(void) ipw2200_m_start(sc);
2387bb5e3b2fSeh146360 			(void) ieee80211_new_state(ic,
2388bb5e3b2fSeh146360 			    IEEE80211_S_SCAN, -1);
2389bb5e3b2fSeh146360 		}
2390bb5e3b2fSeh146360 	}
2391bb5e3b2fSeh146360 	if (err == ERESTART) {
2392bb5e3b2fSeh146360 		if (sc->sc_flags & IPW2200_FLAG_RUNNING)
2393bb5e3b2fSeh146360 			(void) ipw2200_chip_reset(sc);
2394bb5e3b2fSeh146360 	}
2395bb5e3b2fSeh146360 }
2396bb5e3b2fSeh146360 static int
ipw2200_ioctl(struct ipw2200_softc * sc,queue_t * q,mblk_t * m)2397bb5e3b2fSeh146360 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m)
2398bb5e3b2fSeh146360 {
2399bb5e3b2fSeh146360 	struct iocblk	*iocp;
2400922d2c76Seh146360 	uint32_t	len, ret, cmd, mblen;
2401bb5e3b2fSeh146360 	mblk_t		*m0;
2402bb5e3b2fSeh146360 	boolean_t	need_privilege;
2403bb5e3b2fSeh146360 	boolean_t	need_net80211;
2404bb5e3b2fSeh146360 
2405922d2c76Seh146360 	mblen = MBLKL(m);
2406922d2c76Seh146360 	if (mblen < sizeof (struct iocblk)) {
2407bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2408bb5e3b2fSeh146360 		    "ipw2200_ioctl(): ioctl buffer too short, %u\n",
2409922d2c76Seh146360 		    mblen));
2410bb5e3b2fSeh146360 		miocnak(q, m, 0, EINVAL);
2411bb5e3b2fSeh146360 		/*
2412bb5e3b2fSeh146360 		 * Buf not enough, do not need net80211 either
2413bb5e3b2fSeh146360 		 */
2414bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2415bb5e3b2fSeh146360 	}
2416bb5e3b2fSeh146360 
2417bb5e3b2fSeh146360 	/*
2418bb5e3b2fSeh146360 	 * Validate the command
2419bb5e3b2fSeh146360 	 */
2420922d2c76Seh146360 	iocp = (struct iocblk *)(uintptr_t)m->b_rptr;
2421bb5e3b2fSeh146360 	iocp->ioc_error = 0;
2422bb5e3b2fSeh146360 	cmd = iocp->ioc_cmd;
2423bb5e3b2fSeh146360 	need_privilege = B_TRUE;
2424bb5e3b2fSeh146360 	switch (cmd) {
2425bb5e3b2fSeh146360 	case WLAN_SET_PARAM:
2426bb5e3b2fSeh146360 	case WLAN_COMMAND:
2427bb5e3b2fSeh146360 		break;
2428bb5e3b2fSeh146360 	case WLAN_GET_PARAM:
2429bb5e3b2fSeh146360 		need_privilege = B_FALSE;
2430bb5e3b2fSeh146360 		break;
2431bb5e3b2fSeh146360 	default:
2432bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2433bb5e3b2fSeh146360 		    "ipw2200_ioctl(): unknown cmd 0x%x", cmd));
2434bb5e3b2fSeh146360 		miocnak(q, m, 0, EINVAL);
2435bb5e3b2fSeh146360 		/*
2436bb5e3b2fSeh146360 		 * Unknown cmd, do not need net80211 either
2437bb5e3b2fSeh146360 		 */
2438bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2439bb5e3b2fSeh146360 	}
2440bb5e3b2fSeh146360 
2441eae72b5bSSebastien Roy 	if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) {
2442bb5e3b2fSeh146360 		miocnak(q, m, 0, ret);
2443bb5e3b2fSeh146360 		/*
2444bb5e3b2fSeh146360 		 * privilege check fail, do not need net80211 either
2445bb5e3b2fSeh146360 		 */
2446bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2447bb5e3b2fSeh146360 	}
2448eae72b5bSSebastien Roy 
2449bb5e3b2fSeh146360 	/*
2450bb5e3b2fSeh146360 	 * sanity check
2451bb5e3b2fSeh146360 	 */
2452bb5e3b2fSeh146360 	m0 = m->b_cont;
2453bb5e3b2fSeh146360 	if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) ||
2454bb5e3b2fSeh146360 	    m0 == NULL) {
2455bb5e3b2fSeh146360 		miocnak(q, m, 0, EINVAL);
2456bb5e3b2fSeh146360 		/*
2457bb5e3b2fSeh146360 		 * invalid format, do not need net80211 either
2458bb5e3b2fSeh146360 		 */
2459bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2460bb5e3b2fSeh146360 	}
2461bb5e3b2fSeh146360 	/*
2462bb5e3b2fSeh146360 	 * assuming single data block
2463bb5e3b2fSeh146360 	 */
2464bb5e3b2fSeh146360 	if (m0->b_cont) {
2465bb5e3b2fSeh146360 		freemsg(m0->b_cont);
2466bb5e3b2fSeh146360 		m0->b_cont = NULL;
2467bb5e3b2fSeh146360 	}
2468bb5e3b2fSeh146360 
2469bb5e3b2fSeh146360 	need_net80211 = B_FALSE;
2470bb5e3b2fSeh146360 	ret = ipw2200_getset(sc, m0, cmd, &need_net80211);
2471bb5e3b2fSeh146360 	if (!need_net80211) {
2472bb5e3b2fSeh146360 		len = msgdsize(m0);
2473bb5e3b2fSeh146360 
2474bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2475bb5e3b2fSeh146360 		    "ipw2200_ioctl(): go to call miocack with "
2476bb5e3b2fSeh146360 		    "ret = %d, len = %d\n", ret, len));
2477bb5e3b2fSeh146360 		miocack(q, m, len, ret);
2478bb5e3b2fSeh146360 		return (IEEE80211_IOCTL_NOT_REQUIRED);
2479bb5e3b2fSeh146360 	}
2480bb5e3b2fSeh146360 
2481bb5e3b2fSeh146360 	/*
2482bb5e3b2fSeh146360 	 * IEEE80211_IOCTL - need net80211 handle
2483bb5e3b2fSeh146360 	 */
2484bb5e3b2fSeh146360 	return (IEEE80211_IOCTL_REQUIRED);
2485bb5e3b2fSeh146360 }
2486bb5e3b2fSeh146360 
2487bb5e3b2fSeh146360 static int
ipw2200_getset(struct ipw2200_softc * sc,mblk_t * m,uint32_t cmd,boolean_t * need_net80211)2488bb5e3b2fSeh146360 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd,
2489bb5e3b2fSeh146360 	boolean_t *need_net80211)
2490bb5e3b2fSeh146360 {
2491bb5e3b2fSeh146360 	wldp_t		*infp, *outfp;
2492bb5e3b2fSeh146360 	uint32_t	id;
2493bb5e3b2fSeh146360 	int		ret;
2494bb5e3b2fSeh146360 
2495922d2c76Seh146360 	infp  = (wldp_t *)(uintptr_t)m->b_rptr;
2496922d2c76Seh146360 	outfp = (wldp_t *)(uintptr_t)m->b_rptr;
2497bb5e3b2fSeh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
2498bb5e3b2fSeh146360 
2499bb5e3b2fSeh146360 	id = infp->wldp_id;
2500bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT,
2501bb5e3b2fSeh146360 	    "ipw2200_getset(): id = 0x%x\n", id));
2502bb5e3b2fSeh146360 	switch (id) {
2503bb5e3b2fSeh146360 	case WL_RADIO: /* which is not supported by net80211 */
2504bb5e3b2fSeh146360 		ret = iwi_wificfg_radio(sc, cmd, outfp);
2505bb5e3b2fSeh146360 		break;
2506bb5e3b2fSeh146360 	case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */
2507bb5e3b2fSeh146360 		ret = iwi_wificfg_desrates(outfp);
2508bb5e3b2fSeh146360 		break;
2509bb5e3b2fSeh146360 	default:
2510bb5e3b2fSeh146360 		/*
2511bb5e3b2fSeh146360 		 * The wifi IOCTL net80211 supported:
2512bb5e3b2fSeh146360 		 *	case WL_ESSID:
2513bb5e3b2fSeh146360 		 *	case WL_BSSID:
2514bb5e3b2fSeh146360 		 *	case WL_WEP_KEY_TAB:
2515bb5e3b2fSeh146360 		 *	case WL_WEP_KEY_ID:
2516bb5e3b2fSeh146360 		 *	case WL_AUTH_MODE:
2517bb5e3b2fSeh146360 		 *	case WL_ENCRYPTION:
2518bb5e3b2fSeh146360 		 *	case WL_BSS_TYPE:
2519bb5e3b2fSeh146360 		 *	case WL_ESS_LIST:
2520bb5e3b2fSeh146360 		 *	case WL_LINKSTATUS:
2521bb5e3b2fSeh146360 		 *	case WL_RSSI:
2522bb5e3b2fSeh146360 		 *	case WL_SCAN:
2523bb5e3b2fSeh146360 		 *	case WL_LOAD_DEFAULTS:
2524bb5e3b2fSeh146360 		 *	case WL_DISASSOCIATE:
2525bb5e3b2fSeh146360 		 */
25269e2cd38cSeh146360 
25279e2cd38cSeh146360 		/*
25289e2cd38cSeh146360 		 * When radio is off, need to ignore all ioctl.  What need to
25299e2cd38cSeh146360 		 * do is to check radio status firstly.  If radio is ON, pass
25309e2cd38cSeh146360 		 * it to net80211, otherwise, return to upper layer directly.
25319e2cd38cSeh146360 		 *
25329e2cd38cSeh146360 		 * Considering the WL_SUCCESS also means WL_CONNECTED for
25339e2cd38cSeh146360 		 * checking linkstatus, one exception for WL_LINKSTATUS is to
25349e2cd38cSeh146360 		 * let net80211 handle it.
25359e2cd38cSeh146360 		 */
25369e2cd38cSeh146360 		if ((ipw2200_radio_status(sc) == 0) &&
25379e2cd38cSeh146360 		    (id != WL_LINKSTATUS)) {
25389e2cd38cSeh146360 
25399e2cd38cSeh146360 			IPW2200_REPORT((sc->sc_dip, CE_CONT,
25409e2cd38cSeh146360 			    "iwi: radio is OFF\n"));
25419e2cd38cSeh146360 
25429e2cd38cSeh146360 			outfp->wldp_length = WIFI_BUF_OFFSET;
25439e2cd38cSeh146360 			outfp->wldp_result = WL_SUCCESS;
25449e2cd38cSeh146360 			ret = 0;
25459e2cd38cSeh146360 			break;
25469e2cd38cSeh146360 		}
25479e2cd38cSeh146360 
2548bb5e3b2fSeh146360 		*need_net80211 = B_TRUE; /* let net80211 do the rest */
2549bb5e3b2fSeh146360 		return (0);
2550bb5e3b2fSeh146360 	}
2551bb5e3b2fSeh146360 	/*
2552bb5e3b2fSeh146360 	 * we will overwrite everything
2553bb5e3b2fSeh146360 	 */
2554bb5e3b2fSeh146360 	m->b_wptr = m->b_rptr + outfp->wldp_length;
2555bb5e3b2fSeh146360 	return (ret);
2556bb5e3b2fSeh146360 }
2557bb5e3b2fSeh146360 
25587efa17f5Sfei feng - Sun Microsystems - Beijing China /*
25597efa17f5Sfei feng - Sun Microsystems - Beijing China  * Call back functions for get/set proporty
25607efa17f5Sfei feng - Sun Microsystems - Beijing China  */
25617efa17f5Sfei feng - Sun Microsystems - Beijing China 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)25627efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2200_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2563*0dc2366fSVenugopal Iyer     uint_t wldp_length, void *wldp_buf)
25647efa17f5Sfei feng - Sun Microsystems - Beijing China {
25657efa17f5Sfei feng - Sun Microsystems - Beijing China 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
25667efa17f5Sfei feng - Sun Microsystems - Beijing China 	struct ieee80211com	*ic = &sc->sc_ic;
25677efa17f5Sfei feng - Sun Microsystems - Beijing China 	int			err = 0;
25687efa17f5Sfei feng - Sun Microsystems - Beijing China 
25697efa17f5Sfei feng - Sun Microsystems - Beijing China 	switch (wldp_pr_num) {
25707efa17f5Sfei feng - Sun Microsystems - Beijing China 	/* mac_prop_id */
25717efa17f5Sfei feng - Sun Microsystems - Beijing China 	case MAC_PROP_WL_DESIRED_RATES:
25727efa17f5Sfei feng - Sun Microsystems - Beijing China 		IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
25737efa17f5Sfei feng - Sun Microsystems - Beijing China 		    "ipw2200_m_getprop(): Not Support DESIRED_RATES\n"));
25747efa17f5Sfei feng - Sun Microsystems - Beijing China 		break;
25757efa17f5Sfei feng - Sun Microsystems - Beijing China 	case MAC_PROP_WL_RADIO:
25767efa17f5Sfei feng - Sun Microsystems - Beijing China 		*(wl_linkstatus_t *)wldp_buf = ipw2200_radio_status(sc);
25777efa17f5Sfei feng - Sun Microsystems - Beijing China 		break;
25787efa17f5Sfei feng - Sun Microsystems - Beijing China 	default:
25797efa17f5Sfei feng - Sun Microsystems - Beijing China 		/* go through net80211 */
2580*0dc2366fSVenugopal Iyer 		err = ieee80211_getprop(ic, pr_name, wldp_pr_num,
2581*0dc2366fSVenugopal Iyer 		    wldp_length, wldp_buf);
25827efa17f5Sfei feng - Sun Microsystems - Beijing China 		break;
25837efa17f5Sfei feng - Sun Microsystems - Beijing China 	}
25847efa17f5Sfei feng - Sun Microsystems - Beijing China 
25857efa17f5Sfei feng - Sun Microsystems - Beijing China 	return (err);
25867efa17f5Sfei feng - Sun Microsystems - Beijing China }
25877efa17f5Sfei feng - Sun Microsystems - Beijing China 
2588*0dc2366fSVenugopal Iyer 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*0dc2366fSVenugopal Iyer ipw2200_m_propinfo(void *arg, const char *pr_name,
2590*0dc2366fSVenugopal Iyer     mac_prop_id_t wlpd_pr_num, mac_prop_info_handle_t mph)
2591*0dc2366fSVenugopal Iyer {
2592*0dc2366fSVenugopal Iyer 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
2593*0dc2366fSVenugopal Iyer 	struct ieee80211com	*ic = &sc->sc_ic;
2594*0dc2366fSVenugopal Iyer 
2595*0dc2366fSVenugopal Iyer 	ieee80211_propinfo(ic, pr_name, wlpd_pr_num, mph);
2596*0dc2366fSVenugopal Iyer }
2597*0dc2366fSVenugopal Iyer 
25987efa17f5Sfei feng - Sun Microsystems - Beijing China 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)25997efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2200_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
26007efa17f5Sfei feng - Sun Microsystems - Beijing China     uint_t wldp_length, const void *wldp_buf)
26017efa17f5Sfei feng - Sun Microsystems - Beijing China {
26027efa17f5Sfei feng - Sun Microsystems - Beijing China 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)arg;
26037efa17f5Sfei feng - Sun Microsystems - Beijing China 	struct ieee80211com	*ic = &sc->sc_ic;
26047efa17f5Sfei feng - Sun Microsystems - Beijing China 	int			err;
26057efa17f5Sfei feng - Sun Microsystems - Beijing China 
26067efa17f5Sfei feng - Sun Microsystems - Beijing China 	switch (wldp_pr_num) {
26077efa17f5Sfei feng - Sun Microsystems - Beijing China 	/* mac_prop_id */
26087efa17f5Sfei feng - Sun Microsystems - Beijing China 	case MAC_PROP_WL_DESIRED_RATES:
26097efa17f5Sfei feng - Sun Microsystems - Beijing China 		IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
26107efa17f5Sfei feng - Sun Microsystems - Beijing China 		    "ipw2200_m_setprop(): Not Support DESIRED_RATES\n"));
26117efa17f5Sfei feng - Sun Microsystems - Beijing China 		err = ENOTSUP;
26127efa17f5Sfei feng - Sun Microsystems - Beijing China 		break;
26137efa17f5Sfei feng - Sun Microsystems - Beijing China 	case MAC_PROP_WL_RADIO:
26147efa17f5Sfei feng - Sun Microsystems - Beijing China 		IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT,
26157efa17f5Sfei feng - Sun Microsystems - Beijing China 		    "ipw2200_m_setprop(): Not Support RADIO\n"));
26167efa17f5Sfei feng - Sun Microsystems - Beijing China 		err = ENOTSUP;
26177efa17f5Sfei feng - Sun Microsystems - Beijing China 		break;
26187efa17f5Sfei feng - Sun Microsystems - Beijing China 	default:
26197efa17f5Sfei feng - Sun Microsystems - Beijing China 		/* go through net80211 */
26207efa17f5Sfei feng - Sun Microsystems - Beijing China 		err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
26217efa17f5Sfei feng - Sun Microsystems - Beijing China 		    wldp_buf);
26227efa17f5Sfei feng - Sun Microsystems - Beijing China 		break;
26237efa17f5Sfei feng - Sun Microsystems - Beijing China 	}
26247efa17f5Sfei feng - Sun Microsystems - Beijing China 
26257efa17f5Sfei feng - Sun Microsystems - Beijing China 	if (err == ENETRESET) {
26267efa17f5Sfei feng - Sun Microsystems - Beijing China 		if (sc->sc_flags & IPW2200_FLAG_RUNNING) {
26277efa17f5Sfei feng - Sun Microsystems - Beijing China 			(void) ipw2200_m_start(sc);
26287efa17f5Sfei feng - Sun Microsystems - Beijing China 			(void) ieee80211_new_state(ic,
26297efa17f5Sfei feng - Sun Microsystems - Beijing China 			    IEEE80211_S_SCAN, -1);
26307efa17f5Sfei feng - Sun Microsystems - Beijing China 		}
26317efa17f5Sfei feng - Sun Microsystems - Beijing China 		err = 0;
26327efa17f5Sfei feng - Sun Microsystems - Beijing China 	}
26337efa17f5Sfei feng - Sun Microsystems - Beijing China 
26347efa17f5Sfei feng - Sun Microsystems - Beijing China 	return (err);
26357efa17f5Sfei feng - Sun Microsystems - Beijing China }
26367efa17f5Sfei feng - Sun Microsystems - Beijing China 
2637bb5e3b2fSeh146360 static int
iwi_wificfg_radio(struct ipw2200_softc * sc,uint32_t cmd,wldp_t * outfp)2638bb5e3b2fSeh146360 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp)
2639bb5e3b2fSeh146360 {
2640bb5e3b2fSeh146360 	uint32_t	ret = ENOTSUP;
2641bb5e3b2fSeh146360 
2642bb5e3b2fSeh146360 	switch (cmd) {
2643bb5e3b2fSeh146360 	case WLAN_GET_PARAM:
2644bb5e3b2fSeh146360 		*(wl_linkstatus_t *)(outfp->wldp_buf) =
2645bb5e3b2fSeh146360 		    ipw2200_radio_status(sc);
2646bb5e3b2fSeh146360 		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t);
2647bb5e3b2fSeh146360 		outfp->wldp_result = WL_SUCCESS;
2648bb5e3b2fSeh146360 		ret = 0; /* command success */
2649bb5e3b2fSeh146360 		break;
2650bb5e3b2fSeh146360 	case WLAN_SET_PARAM:
2651bb5e3b2fSeh146360 	default:
2652bb5e3b2fSeh146360 		break;
2653bb5e3b2fSeh146360 	}
2654bb5e3b2fSeh146360 	return (ret);
2655bb5e3b2fSeh146360 }
2656bb5e3b2fSeh146360 
2657bb5e3b2fSeh146360 static int
iwi_wificfg_desrates(wldp_t * outfp)2658bb5e3b2fSeh146360 iwi_wificfg_desrates(wldp_t *outfp)
2659bb5e3b2fSeh146360 {
2660bb5e3b2fSeh146360 	/* return success, but with result NOTSUPPORTED */
2661bb5e3b2fSeh146360 	outfp->wldp_length = WIFI_BUF_OFFSET;
2662bb5e3b2fSeh146360 	outfp->wldp_result = WL_NOTSUPPORTED;
2663bb5e3b2fSeh146360 	return (0);
2664bb5e3b2fSeh146360 }
2665bb5e3b2fSeh146360 /* End of IOCTL Handlers */
2666bb5e3b2fSeh146360 
2667bb5e3b2fSeh146360 void
ipw2200_fix_channel(struct ieee80211com * ic,mblk_t * m)2668bb5e3b2fSeh146360 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m)
2669bb5e3b2fSeh146360 {
2670bb5e3b2fSeh146360 	struct ieee80211_frame	*wh;
2671bb5e3b2fSeh146360 	uint8_t			subtype;
2672bb5e3b2fSeh146360 	uint8_t			*frm, *efrm;
2673bb5e3b2fSeh146360 
2674bb5e3b2fSeh146360 	wh = (struct ieee80211_frame *)m->b_rptr;
2675bb5e3b2fSeh146360 
2676bb5e3b2fSeh146360 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
2677bb5e3b2fSeh146360 		return;
2678bb5e3b2fSeh146360 
2679bb5e3b2fSeh146360 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
2680bb5e3b2fSeh146360 
2681bb5e3b2fSeh146360 	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
2682bb5e3b2fSeh146360 	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
2683bb5e3b2fSeh146360 		return;
2684bb5e3b2fSeh146360 
2685bb5e3b2fSeh146360 	/*
2686bb5e3b2fSeh146360 	 * assume the message contains only 1 block
2687bb5e3b2fSeh146360 	 */
2688bb5e3b2fSeh146360 	frm   = (uint8_t *)(wh + 1);
2689bb5e3b2fSeh146360 	efrm  = (uint8_t *)m->b_wptr;
2690bb5e3b2fSeh146360 	frm  += 12;  /* skip tstamp, bintval and capinfo fields */
2691bb5e3b2fSeh146360 	while (frm < efrm) {
2692bb5e3b2fSeh146360 		if (*frm == IEEE80211_ELEMID_DSPARMS)
2693bb5e3b2fSeh146360 #if IEEE80211_CHAN_MAX < 255
2694bb5e3b2fSeh146360 		if (frm[2] <= IEEE80211_CHAN_MAX)
2695bb5e3b2fSeh146360 #endif
2696bb5e3b2fSeh146360 			ic->ic_curchan = &ic->ic_sup_channels[frm[2]];
2697bb5e3b2fSeh146360 		frm += frm[1] + 2;
2698bb5e3b2fSeh146360 	}
2699bb5e3b2fSeh146360 }
2700bb5e3b2fSeh146360 
2701bb5e3b2fSeh146360 static void
ipw2200_rcv_frame(struct ipw2200_softc * sc,struct ipw2200_frame * frame)2702bb5e3b2fSeh146360 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame)
2703bb5e3b2fSeh146360 {
2704bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2705bb5e3b2fSeh146360 	uint8_t			*data = (uint8_t *)frame;
2706bb5e3b2fSeh146360 	uint32_t		len;
2707bb5e3b2fSeh146360 	struct ieee80211_frame	*wh;
2708bb5e3b2fSeh146360 	struct ieee80211_node	*in;
2709bb5e3b2fSeh146360 	mblk_t			*m;
2710bb5e3b2fSeh146360 
2711bb5e3b2fSeh146360 	len = LE_16(frame->len);
2712bb5e3b2fSeh146360 	if ((len < sizeof (struct ieee80211_frame_min)) ||
2713bb5e3b2fSeh146360 	    (len > IPW2200_RXBUF_SIZE)) {
2714bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2715bb5e3b2fSeh146360 		    "ipw2200_rcv_frame(): bad frame length=%u\n",
2716bb5e3b2fSeh146360 		    LE_16(frame->len)));
2717922d2c76Seh146360 		sc->sc_stats.sc_rx_len_err++; /* length doesn't work */
2718bb5e3b2fSeh146360 		return;
2719bb5e3b2fSeh146360 	}
2720bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2721bb5e3b2fSeh146360 	    "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len));
2722bb5e3b2fSeh146360 
2723924f3e72Seh146360 	/*
2724924f3e72Seh146360 	 * Skip the frame header, get the real data from the input
2725924f3e72Seh146360 	 */
2726924f3e72Seh146360 	data += sizeof (struct ipw2200_frame);
2727924f3e72Seh146360 
2728bb5e3b2fSeh146360 	m = allocb(len, BPRI_MED);
2729bb5e3b2fSeh146360 	if (m) {
2730bb5e3b2fSeh146360 		(void) memcpy(m->b_wptr, data, len);
2731bb5e3b2fSeh146360 		m->b_wptr += len;
2732bb5e3b2fSeh146360 
2733bb5e3b2fSeh146360 		if (ic->ic_state == IEEE80211_S_SCAN) {
2734bb5e3b2fSeh146360 			ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan];
2735bb5e3b2fSeh146360 			ipw2200_fix_channel(ic, m);
2736bb5e3b2fSeh146360 		}
2737924f3e72Seh146360 		wh = (struct ieee80211_frame *)m->b_rptr;
2738bb5e3b2fSeh146360 
2739bb5e3b2fSeh146360 		in = ieee80211_find_rxnode(ic, wh);
2740924f3e72Seh146360 
2741bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT,
2742bb5e3b2fSeh146360 		    "ipw2200_rcv_frame(): "
2743924f3e72Seh146360 		    "type = %x, subtype = %x, i_fc[1] = %x, "
2744bb5e3b2fSeh146360 		    "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n",
2745924f3e72Seh146360 		    wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK,
2746924f3e72Seh146360 		    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK,
2747924f3e72Seh146360 		    wh->i_fc[1] & IEEE80211_FC1_WEP,
2748bb5e3b2fSeh146360 		    in->in_esslen,
2749bb5e3b2fSeh146360 		    in->in_essid[0], in->in_essid[1], in->in_essid[2],
2750bb5e3b2fSeh146360 		    in->in_essid[3], in->in_essid[4], in->in_essid[5]));
2751bb5e3b2fSeh146360 
2752bb5e3b2fSeh146360 		(void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0);
2753bb5e3b2fSeh146360 
2754bb5e3b2fSeh146360 		ieee80211_free_node(in);
2755bb5e3b2fSeh146360 	}
2756bb5e3b2fSeh146360 	else
2757bb5e3b2fSeh146360 		IPW2200_WARN((sc->sc_dip, CE_WARN,
2758bb5e3b2fSeh146360 		    "ipw2200_rcv_frame(): "
2759bb5e3b2fSeh146360 		    "cannot allocate receive message(%u)\n",
2760bb5e3b2fSeh146360 		    LE_16(frame->len)));
2761bb5e3b2fSeh146360 }
2762bb5e3b2fSeh146360 
2763bb5e3b2fSeh146360 static void
ipw2200_rcv_notif(struct ipw2200_softc * sc,struct ipw2200_notif * notif)2764bb5e3b2fSeh146360 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif)
2765bb5e3b2fSeh146360 {
2766bb5e3b2fSeh146360 	struct ieee80211com			*ic = &sc->sc_ic;
2767bb5e3b2fSeh146360 	struct ipw2200_notif_association	*assoc;
2768bb5e3b2fSeh146360 	struct ipw2200_notif_authentication	*auth;
2769bb5e3b2fSeh146360 	uint8_t					*ndata = (uint8_t *)notif;
2770bb5e3b2fSeh146360 
2771bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2772bb5e3b2fSeh146360 	    "ipw2200_rcv_notif(): type=%u\n", notif->type));
2773bb5e3b2fSeh146360 
2774bb5e3b2fSeh146360 	ndata += sizeof (struct ipw2200_notif);
2775bb5e3b2fSeh146360 	switch (notif->type) {
2776bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_ASSOCIATION:
2777bb5e3b2fSeh146360 		assoc = (struct ipw2200_notif_association *)ndata;
2778bb5e3b2fSeh146360 
2779bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2780bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): association=%u,%u\n",
2781bb5e3b2fSeh146360 		    assoc->state, assoc->status));
2782bb5e3b2fSeh146360 
2783bb5e3b2fSeh146360 		switch (assoc->state) {
2784bb5e3b2fSeh146360 		case IPW2200_ASSOC_SUCCESS:
2785924f3e72Seh146360 			sc->sc_flags |= IPW2200_FLAG_ASSOCIATED;
2786bb5e3b2fSeh146360 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2787bb5e3b2fSeh146360 			break;
2788bb5e3b2fSeh146360 		case IPW2200_ASSOC_FAIL:
2789924f3e72Seh146360 			sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED;
2790924f3e72Seh146360 			ieee80211_begin_scan(ic, 1);
2791bb5e3b2fSeh146360 			break;
2792bb5e3b2fSeh146360 		default:
2793bb5e3b2fSeh146360 			break;
2794bb5e3b2fSeh146360 		}
2795bb5e3b2fSeh146360 		break;
2796bb5e3b2fSeh146360 
2797bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_AUTHENTICATION:
2798bb5e3b2fSeh146360 		auth = (struct ipw2200_notif_authentication *)ndata;
2799bb5e3b2fSeh146360 
2800bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT,
2801bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): authentication=%u\n", auth->state));
2802bb5e3b2fSeh146360 
2803bb5e3b2fSeh146360 		switch (auth->state) {
2804bb5e3b2fSeh146360 		case IPW2200_AUTH_SUCCESS:
2805bb5e3b2fSeh146360 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
2806bb5e3b2fSeh146360 			break;
2807bb5e3b2fSeh146360 		case IPW2200_AUTH_FAIL:
2808924f3e72Seh146360 			sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED;
2809bb5e3b2fSeh146360 			break;
2810bb5e3b2fSeh146360 		default:
2811bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2812bb5e3b2fSeh146360 			    "ipw2200_rcv_notif(): "
2813bb5e3b2fSeh146360 			    "unknown authentication state(%u)\n", auth->state));
2814bb5e3b2fSeh146360 			break;
2815bb5e3b2fSeh146360 		}
2816bb5e3b2fSeh146360 		break;
2817bb5e3b2fSeh146360 
2818bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_SCAN_CHANNEL:
2819bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
2820bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): scan-channel=%u\n",
2821bb5e3b2fSeh146360 		    ((struct ipw2200_notif_scan_channel *)ndata)->nchan));
2822bb5e3b2fSeh146360 		break;
2823bb5e3b2fSeh146360 
2824bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_SCAN_COMPLETE:
2825bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT,
2826bb5e3b2fSeh146360 		    "ipw2200_rcv_notif():scan-completed,(%u,%u)\n",
2827bb5e3b2fSeh146360 		    ((struct ipw2200_notif_scan_complete *)ndata)->nchan,
2828bb5e3b2fSeh146360 		    ((struct ipw2200_notif_scan_complete *)ndata)->status));
2829bb5e3b2fSeh146360 
2830bb5e3b2fSeh146360 		/*
2831bb5e3b2fSeh146360 		 * scan complete
2832bb5e3b2fSeh146360 		 */
2833bb5e3b2fSeh146360 		sc->sc_flags &= ~IPW2200_FLAG_SCANNING;
2834bb5e3b2fSeh146360 		ieee80211_end_scan(ic);
2835bb5e3b2fSeh146360 		break;
2836bb5e3b2fSeh146360 
2837bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_BEACON:
2838bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_CALIBRATION:
2839bb5e3b2fSeh146360 	case IPW2200_NOTIF_TYPE_NOISE:
2840bb5e3b2fSeh146360 		/*
2841bb5e3b2fSeh146360 		 * just ignore
2842bb5e3b2fSeh146360 		 */
2843bb5e3b2fSeh146360 		break;
2844bb5e3b2fSeh146360 	default:
2845bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT,
2846bb5e3b2fSeh146360 		    "ipw2200_rcv_notif(): unknown notification type(%u)\n",
2847bb5e3b2fSeh146360 		    notif->type));
2848bb5e3b2fSeh146360 		break;
2849bb5e3b2fSeh146360 	}
2850bb5e3b2fSeh146360 }
2851bb5e3b2fSeh146360 
2852bb5e3b2fSeh146360 static uint_t
ipw2200_intr(caddr_t arg)2853bb5e3b2fSeh146360 ipw2200_intr(caddr_t arg)
2854bb5e3b2fSeh146360 {
2855922d2c76Seh146360 	struct ipw2200_softc	*sc = (struct ipw2200_softc *)(uintptr_t)arg;
2856bb5e3b2fSeh146360 	struct ieee80211com	*ic = &sc->sc_ic;
2857bb5e3b2fSeh146360 	uint32_t		ireg, ridx, len, i;
2858bb5e3b2fSeh146360 	uint8_t			*p, *rxbuf;
2859bb5e3b2fSeh146360 	struct dma_region	*dr;
2860bb5e3b2fSeh146360 	struct ipw2200_hdr	*hdr;
2861bb5e3b2fSeh146360 	uint32_t		widx;
2862bb5e3b2fSeh146360 
2863922d2c76Seh146360 	/* when it is on suspend, unclaim all interrupt directly */
2864922d2c76Seh146360 	if (sc->sc_flags & IPW2200_FLAG_SUSPEND)
2865bb5e3b2fSeh146360 		return (DDI_INTR_UNCLAIMED);
2866bb5e3b2fSeh146360 
2867922d2c76Seh146360 	/* unclaim interrupt when it is not for iwi */
2868922d2c76Seh146360 	ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR);
2869922d2c76Seh146360 	if (ireg == 0xffffffff ||
2870922d2c76Seh146360 	    !(ireg & IPW2200_INTR_MASK_ALL))
2871bb5e3b2fSeh146360 		return (DDI_INTR_UNCLAIMED);
2872bb5e3b2fSeh146360 
2873bb5e3b2fSeh146360 	/*
2874bb5e3b2fSeh146360 	 * mask all interrupts
2875bb5e3b2fSeh146360 	 */
2876bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0);
2877bb5e3b2fSeh146360 
2878bb5e3b2fSeh146360 	/*
2879bb5e3b2fSeh146360 	 * acknowledge all fired interrupts
2880bb5e3b2fSeh146360 	 */
2881bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg);
2882bb5e3b2fSeh146360 
2883bb5e3b2fSeh146360 	IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2884bb5e3b2fSeh146360 	    "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg));
2885bb5e3b2fSeh146360 
2886bb5e3b2fSeh146360 	if (ireg & IPW2200_INTR_MASK_ERR) {
2887bb5e3b2fSeh146360 
2888bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT,
2889bb5e3b2fSeh146360 		    "ipw2200 interrupt(): int= 0x%08x\n", ireg));
2890bb5e3b2fSeh146360 
2891bb5e3b2fSeh146360 		/*
2892bb5e3b2fSeh146360 		 * inform mfthread to recover hw error by stopping it
2893bb5e3b2fSeh146360 		 */
2894bb5e3b2fSeh146360 		mutex_enter(&sc->sc_mflock);
2895bb5e3b2fSeh146360 		sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER;
2896bb5e3b2fSeh146360 		mutex_exit(&sc->sc_mflock);
2897bb5e3b2fSeh146360 
2898922d2c76Seh146360 		goto enable_interrupt;
2899922d2c76Seh146360 	}
2900922d2c76Seh146360 
2901924f3e72Seh146360 	/*
2902924f3e72Seh146360 	 * FW intr
2903924f3e72Seh146360 	 */
2904bb5e3b2fSeh146360 	if (ireg & IPW2200_INTR_FW_INITED) {
2905bb5e3b2fSeh146360 		mutex_enter(&sc->sc_ilock);
2906bb5e3b2fSeh146360 		sc->sc_fw_ok = 1;
2907bb5e3b2fSeh146360 		cv_signal(&sc->sc_fw_cond);
2908bb5e3b2fSeh146360 		mutex_exit(&sc->sc_ilock);
2909bb5e3b2fSeh146360 	}
2910924f3e72Seh146360 
2911924f3e72Seh146360 	/*
2912924f3e72Seh146360 	 * Radio OFF
2913924f3e72Seh146360 	 */
2914bb5e3b2fSeh146360 	if (ireg & IPW2200_INTR_RADIO_OFF) {
2915bb5e3b2fSeh146360 		IPW2200_REPORT((sc->sc_dip, CE_CONT,
2916bb5e3b2fSeh146360 		    "ipw2200_intr(): radio is OFF\n"));
29179e2cd38cSeh146360 
2918bb5e3b2fSeh146360 		/*
29199e2cd38cSeh146360 		 * Stop hardware, will notify LINK is down.
29209e2cd38cSeh146360 		 * Need a better scan solution to ensure
29219e2cd38cSeh146360 		 * table has right value.
2922bb5e3b2fSeh146360 		 */
2923bb5e3b2fSeh146360 		ipw2200_stop(sc);
2924bb5e3b2fSeh146360 	}
2925924f3e72Seh146360 
2926924f3e72Seh146360 	/*
2927924f3e72Seh146360 	 * CMD intr
2928924f3e72Seh146360 	 */
2929bb5e3b2fSeh146360 	if (ireg & IPW2200_INTR_CMD_TRANSFER) {
2930bb5e3b2fSeh146360 		mutex_enter(&sc->sc_cmd_lock);
2931bb5e3b2fSeh146360 		ridx = ipw2200_csr_get32(sc,
2932bb5e3b2fSeh146360 		    IPW2200_CSR_CMD_READ_INDEX);
2933bb5e3b2fSeh146360 		i = RING_FORWARD(sc->sc_cmd_cur,
2934bb5e3b2fSeh146360 		    sc->sc_cmd_free, IPW2200_CMD_RING_SIZE);
2935bb5e3b2fSeh146360 		len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE);
2936bb5e3b2fSeh146360 
2937bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2938bb5e3b2fSeh146360 		    "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n",
2939bb5e3b2fSeh146360 		    i, ridx, len));
2940bb5e3b2fSeh146360 
2941bb5e3b2fSeh146360 		if (len > 0) {
2942bb5e3b2fSeh146360 			sc->sc_cmd_free += len;
2943bb5e3b2fSeh146360 			cv_signal(&sc->sc_cmd_cond);
2944bb5e3b2fSeh146360 		}
2945bb5e3b2fSeh146360 		for (; i != ridx;
2946bb5e3b2fSeh146360 		    i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE))
2947bb5e3b2fSeh146360 			sc->sc_done[i] = 1;
2948bb5e3b2fSeh146360 		mutex_exit(&sc->sc_cmd_lock);
2949bb5e3b2fSeh146360 
2950bb5e3b2fSeh146360 		mutex_enter(&sc->sc_ilock);
2951bb5e3b2fSeh146360 		cv_signal(&sc->sc_cmd_status_cond);
2952bb5e3b2fSeh146360 		mutex_exit(&sc->sc_ilock);
2953bb5e3b2fSeh146360 	}
2954924f3e72Seh146360 
2955924f3e72Seh146360 	/*
2956924f3e72Seh146360 	 * RX intr
2957924f3e72Seh146360 	 */
2958bb5e3b2fSeh146360 	if (ireg & IPW2200_INTR_RX_TRANSFER) {
2959bb5e3b2fSeh146360 		ridx = ipw2200_csr_get32(sc,
2960bb5e3b2fSeh146360 		    IPW2200_CSR_RX_READ_INDEX);
2961bb5e3b2fSeh146360 		widx = ipw2200_csr_get32(sc,
2962bb5e3b2fSeh146360 		    IPW2200_CSR_RX_WRITE_INDEX);
2963bb5e3b2fSeh146360 
2964bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2965bb5e3b2fSeh146360 		    "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n",
2966bb5e3b2fSeh146360 		    ridx, widx));
2967bb5e3b2fSeh146360 
2968bb5e3b2fSeh146360 		for (; sc->sc_rx_cur != ridx;
2969bb5e3b2fSeh146360 		    sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1,
2970bb5e3b2fSeh146360 		    IPW2200_RX_RING_SIZE)) {
2971bb5e3b2fSeh146360 			i	= sc->sc_rx_cur;
2972bb5e3b2fSeh146360 			rxbuf	= sc->sc_rxbufs[i];
2973bb5e3b2fSeh146360 			dr	= &sc->sc_dma_rxbufs[i];
2974bb5e3b2fSeh146360 
2975bb5e3b2fSeh146360 			/*
2976bb5e3b2fSeh146360 			 * DMA sync
2977bb5e3b2fSeh146360 			 */
2978bb5e3b2fSeh146360 			(void) ddi_dma_sync(dr->dr_hnd, 0,
2979bb5e3b2fSeh146360 			    IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL);
2980bb5e3b2fSeh146360 			/*
2981bb5e3b2fSeh146360 			 * Get rx header(hdr) and rx data(p) from rxbuf
2982bb5e3b2fSeh146360 			 */
2983bb5e3b2fSeh146360 			p	= rxbuf;
2984bb5e3b2fSeh146360 			hdr	= (struct ipw2200_hdr *)p;
2985bb5e3b2fSeh146360 			p	+= sizeof (struct ipw2200_hdr);
2986bb5e3b2fSeh146360 
2987922d2c76Seh146360 			IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT,
2988bb5e3b2fSeh146360 			    "ipw2200_intr(): Rx hdr type %u\n",
2989bb5e3b2fSeh146360 			    hdr->type));
2990bb5e3b2fSeh146360 
2991bb5e3b2fSeh146360 			switch (hdr->type) {
2992bb5e3b2fSeh146360 			case IPW2200_HDR_TYPE_FRAME:
2993bb5e3b2fSeh146360 				ipw2200_rcv_frame(sc,
2994bb5e3b2fSeh146360 				    (struct ipw2200_frame *)p);
2995bb5e3b2fSeh146360 				break;
2996bb5e3b2fSeh146360 
2997bb5e3b2fSeh146360 			case IPW2200_HDR_TYPE_NOTIF:
2998bb5e3b2fSeh146360 				ipw2200_rcv_notif(sc,
2999bb5e3b2fSeh146360 				    (struct ipw2200_notif *)p);
3000bb5e3b2fSeh146360 				break;
3001922d2c76Seh146360 
3002bb5e3b2fSeh146360 			default:
3003922d2c76Seh146360 				IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip,
3004922d2c76Seh146360 				    CE_CONT,
3005922d2c76Seh146360 				    "ipw2200_intr(): unknown Rx hdr type %u\n",
3006bb5e3b2fSeh146360 				    hdr->type));
3007bb5e3b2fSeh146360 				break;
3008bb5e3b2fSeh146360 			}
3009bb5e3b2fSeh146360 		}
3010bb5e3b2fSeh146360 		/*
3011bb5e3b2fSeh146360 		 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX
3012bb5e3b2fSeh146360 		 */
3013bb5e3b2fSeh146360 		ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX,
3014bb5e3b2fSeh146360 		    RING_BACKWARD(sc->sc_rx_cur, 1,
3015bb5e3b2fSeh146360 		    IPW2200_RX_RING_SIZE));
3016bb5e3b2fSeh146360 	}
3017924f3e72Seh146360 
3018924f3e72Seh146360 	/*
3019924f3e72Seh146360 	 * TX intr
3020924f3e72Seh146360 	 */
3021bb5e3b2fSeh146360 	if (ireg & IPW2200_INTR_TX1_TRANSFER) {
3022bb5e3b2fSeh146360 		mutex_enter(&sc->sc_tx_lock);
3023bb5e3b2fSeh146360 		ridx = ipw2200_csr_get32(sc,
3024bb5e3b2fSeh146360 		    IPW2200_CSR_TX1_READ_INDEX);
3025bb5e3b2fSeh146360 		len  = RING_FLEN(RING_FORWARD(sc->sc_tx_cur,
3026bb5e3b2fSeh146360 		    sc->sc_tx_free, IPW2200_TX_RING_SIZE),
3027bb5e3b2fSeh146360 		    ridx, IPW2200_TX_RING_SIZE);
3028924f3e72Seh146360 		sc->sc_tx_free += len;
3029bb5e3b2fSeh146360 		IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT,
3030bb5e3b2fSeh146360 		    "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n",
3031bb5e3b2fSeh146360 		    ridx, len));
3032924f3e72Seh146360 		mutex_exit(&sc->sc_tx_lock);
3033bb5e3b2fSeh146360 
3034924f3e72Seh146360 		mutex_enter(&sc->sc_resched_lock);
3035bb5e3b2fSeh146360 		if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) &&
3036bb5e3b2fSeh146360 		    (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) {
3037bb5e3b2fSeh146360 			IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip,
3038bb5e3b2fSeh146360 			    CE_CONT,
3039bb5e3b2fSeh146360 			    "ipw2200_intr(): Need Reschedule!"));
3040bb5e3b2fSeh146360 			sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED;
3041bb5e3b2fSeh146360 			mac_tx_update(ic->ic_mach);
3042bb5e3b2fSeh146360 		}
3043924f3e72Seh146360 		mutex_exit(&sc->sc_resched_lock);
3044924f3e72Seh146360 	}
3045bb5e3b2fSeh146360 
3046922d2c76Seh146360 enable_interrupt:
3047bb5e3b2fSeh146360 	/*
3048bb5e3b2fSeh146360 	 * enable all interrupts
3049bb5e3b2fSeh146360 	 */
3050bb5e3b2fSeh146360 	ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL);
3051bb5e3b2fSeh146360 
3052bb5e3b2fSeh146360 	return (DDI_INTR_CLAIMED);
3053bb5e3b2fSeh146360 }
3054bb5e3b2fSeh146360 
3055bb5e3b2fSeh146360 
3056bb5e3b2fSeh146360 /*
3057bb5e3b2fSeh146360  *  Module Loading Data & Entry Points
3058bb5e3b2fSeh146360  */
3059bb5e3b2fSeh146360 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach,
306014d91224Sfei feng - Sun Microsystems - Beijing China     ipw2200_detach, nodev, NULL, D_MP, NULL, ipw2200_quiesce);
3061bb5e3b2fSeh146360 
3062bb5e3b2fSeh146360 static struct modldrv ipw2200_modldrv = {
3063bb5e3b2fSeh146360 	&mod_driverops,
3064bb5e3b2fSeh146360 	ipw2200_ident,
3065bb5e3b2fSeh146360 	&ipw2200_devops
3066bb5e3b2fSeh146360 };
3067bb5e3b2fSeh146360 
3068bb5e3b2fSeh146360 static struct modlinkage ipw2200_modlinkage = {
3069bb5e3b2fSeh146360 	MODREV_1,
3070bb5e3b2fSeh146360 	&ipw2200_modldrv,
3071bb5e3b2fSeh146360 	NULL
3072bb5e3b2fSeh146360 };
3073bb5e3b2fSeh146360 
3074bb5e3b2fSeh146360 int
_init(void)3075bb5e3b2fSeh146360 _init(void)
3076bb5e3b2fSeh146360 {
3077bb5e3b2fSeh146360 	int status;
3078bb5e3b2fSeh146360 
3079bb5e3b2fSeh146360 	status = ddi_soft_state_init(&ipw2200_ssp,
3080bb5e3b2fSeh146360 	    sizeof (struct ipw2200_softc), 1);
3081bb5e3b2fSeh146360 	if (status != DDI_SUCCESS)
3082bb5e3b2fSeh146360 		return (status);
3083bb5e3b2fSeh146360 
3084bb5e3b2fSeh146360 	mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME);
3085bb5e3b2fSeh146360 	status = mod_install(&ipw2200_modlinkage);
3086bb5e3b2fSeh146360 	if (status != DDI_SUCCESS) {
3087bb5e3b2fSeh146360 		mac_fini_ops(&ipw2200_devops);
3088bb5e3b2fSeh146360 		ddi_soft_state_fini(&ipw2200_ssp);
3089bb5e3b2fSeh146360 	}
3090bb5e3b2fSeh146360 
3091bb5e3b2fSeh146360 	return (status);
3092bb5e3b2fSeh146360 }
3093bb5e3b2fSeh146360 
3094bb5e3b2fSeh146360 int
_fini(void)3095bb5e3b2fSeh146360 _fini(void)
3096bb5e3b2fSeh146360 {
3097bb5e3b2fSeh146360 	int status;
3098bb5e3b2fSeh146360 
3099bb5e3b2fSeh146360 	status = mod_remove(&ipw2200_modlinkage);
3100bb5e3b2fSeh146360 	if (status == DDI_SUCCESS) {
3101bb5e3b2fSeh146360 		mac_fini_ops(&ipw2200_devops);
3102bb5e3b2fSeh146360 		ddi_soft_state_fini(&ipw2200_ssp);
3103bb5e3b2fSeh146360 	}
3104bb5e3b2fSeh146360 
3105bb5e3b2fSeh146360 	return (status);
3106bb5e3b2fSeh146360 }
3107bb5e3b2fSeh146360 
3108bb5e3b2fSeh146360 int
_info(struct modinfo * modinfop)3109bb5e3b2fSeh146360 _info(struct modinfo *modinfop)
3110bb5e3b2fSeh146360 {
3111bb5e3b2fSeh146360 	return (mod_info(&ipw2200_modlinkage, modinfop));
3112bb5e3b2fSeh146360 }
3113