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