xref: /onnv-gate/usr/src/uts/common/io/arn/arn_main.c (revision 9999:d5e89571de4e)
1*9999SWang.Lin@Sun.COM /*
2*9999SWang.Lin@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3*9999SWang.Lin@Sun.COM  * Use is subject to license terms.
4*9999SWang.Lin@Sun.COM  */
5*9999SWang.Lin@Sun.COM 
6*9999SWang.Lin@Sun.COM /*
7*9999SWang.Lin@Sun.COM  * Copyright (c) 2008 Atheros Communications Inc.
8*9999SWang.Lin@Sun.COM  *
9*9999SWang.Lin@Sun.COM  * Permission to use, copy, modify, and/or distribute this software for any
10*9999SWang.Lin@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
11*9999SWang.Lin@Sun.COM  * copyright notice and this permission notice appear in all copies.
12*9999SWang.Lin@Sun.COM  *
13*9999SWang.Lin@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14*9999SWang.Lin@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15*9999SWang.Lin@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16*9999SWang.Lin@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17*9999SWang.Lin@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18*9999SWang.Lin@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19*9999SWang.Lin@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20*9999SWang.Lin@Sun.COM  */
21*9999SWang.Lin@Sun.COM 
22*9999SWang.Lin@Sun.COM #include <sys/param.h>
23*9999SWang.Lin@Sun.COM #include <sys/types.h>
24*9999SWang.Lin@Sun.COM #include <sys/signal.h>
25*9999SWang.Lin@Sun.COM #include <sys/stream.h>
26*9999SWang.Lin@Sun.COM #include <sys/termio.h>
27*9999SWang.Lin@Sun.COM #include <sys/errno.h>
28*9999SWang.Lin@Sun.COM #include <sys/file.h>
29*9999SWang.Lin@Sun.COM #include <sys/cmn_err.h>
30*9999SWang.Lin@Sun.COM #include <sys/stropts.h>
31*9999SWang.Lin@Sun.COM #include <sys/strsubr.h>
32*9999SWang.Lin@Sun.COM #include <sys/strtty.h>
33*9999SWang.Lin@Sun.COM #include <sys/kbio.h>
34*9999SWang.Lin@Sun.COM #include <sys/cred.h>
35*9999SWang.Lin@Sun.COM #include <sys/stat.h>
36*9999SWang.Lin@Sun.COM #include <sys/consdev.h>
37*9999SWang.Lin@Sun.COM #include <sys/kmem.h>
38*9999SWang.Lin@Sun.COM #include <sys/modctl.h>
39*9999SWang.Lin@Sun.COM #include <sys/ddi.h>
40*9999SWang.Lin@Sun.COM #include <sys/sunddi.h>
41*9999SWang.Lin@Sun.COM #include <sys/pci.h>
42*9999SWang.Lin@Sun.COM #include <sys/errno.h>
43*9999SWang.Lin@Sun.COM #include <sys/mac_provider.h>
44*9999SWang.Lin@Sun.COM #include <sys/dlpi.h>
45*9999SWang.Lin@Sun.COM #include <sys/ethernet.h>
46*9999SWang.Lin@Sun.COM #include <sys/list.h>
47*9999SWang.Lin@Sun.COM #include <sys/byteorder.h>
48*9999SWang.Lin@Sun.COM #include <sys/strsun.h>
49*9999SWang.Lin@Sun.COM #include <sys/policy.h>
50*9999SWang.Lin@Sun.COM #include <inet/common.h>
51*9999SWang.Lin@Sun.COM #include <inet/nd.h>
52*9999SWang.Lin@Sun.COM #include <inet/mi.h>
53*9999SWang.Lin@Sun.COM #include <inet/wifi_ioctl.h>
54*9999SWang.Lin@Sun.COM #include <sys/mac_wifi.h>
55*9999SWang.Lin@Sun.COM 
56*9999SWang.Lin@Sun.COM #include "arn_ath9k.h"
57*9999SWang.Lin@Sun.COM #include "arn_core.h"
58*9999SWang.Lin@Sun.COM #include "arn_reg.h"
59*9999SWang.Lin@Sun.COM #include "arn_hw.h"
60*9999SWang.Lin@Sun.COM 
61*9999SWang.Lin@Sun.COM #define	ARN_MAX_RSSI	63	/* max rssi */
62*9999SWang.Lin@Sun.COM 
63*9999SWang.Lin@Sun.COM /*
64*9999SWang.Lin@Sun.COM  * PIO access attributes for registers
65*9999SWang.Lin@Sun.COM  */
66*9999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_reg_accattr = {
67*9999SWang.Lin@Sun.COM 	DDI_DEVICE_ATTR_V0,
68*9999SWang.Lin@Sun.COM 	DDI_STRUCTURE_LE_ACC,
69*9999SWang.Lin@Sun.COM 	DDI_STRICTORDER_ACC
70*9999SWang.Lin@Sun.COM };
71*9999SWang.Lin@Sun.COM 
72*9999SWang.Lin@Sun.COM /*
73*9999SWang.Lin@Sun.COM  * DMA access attributes for descriptors: NOT to be byte swapped.
74*9999SWang.Lin@Sun.COM  */
75*9999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_desc_accattr = {
76*9999SWang.Lin@Sun.COM 	DDI_DEVICE_ATTR_V0,
77*9999SWang.Lin@Sun.COM 	DDI_STRUCTURE_LE_ACC,
78*9999SWang.Lin@Sun.COM 	DDI_STRICTORDER_ACC
79*9999SWang.Lin@Sun.COM };
80*9999SWang.Lin@Sun.COM 
81*9999SWang.Lin@Sun.COM /*
82*9999SWang.Lin@Sun.COM  * Describes the chip's DMA engine
83*9999SWang.Lin@Sun.COM  */
84*9999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_dma_attr = {
85*9999SWang.Lin@Sun.COM 	DMA_ATTR_V0,	/* version number */
86*9999SWang.Lin@Sun.COM 	0,				/* low address */
87*9999SWang.Lin@Sun.COM 	0xffffffffU,	/* high address */
88*9999SWang.Lin@Sun.COM 	0x3ffffU,		/* counter register max */
89*9999SWang.Lin@Sun.COM 	1,				/* alignment */
90*9999SWang.Lin@Sun.COM 	0xFFF,			/* burst sizes */
91*9999SWang.Lin@Sun.COM 	1,				/* minimum transfer size */
92*9999SWang.Lin@Sun.COM 	0x3ffffU,		/* max transfer size */
93*9999SWang.Lin@Sun.COM 	0xffffffffU,	/* address register max */
94*9999SWang.Lin@Sun.COM 	1,				/* no scatter-gather */
95*9999SWang.Lin@Sun.COM 	1,				/* granularity of device */
96*9999SWang.Lin@Sun.COM 	0,				/* DMA flags */
97*9999SWang.Lin@Sun.COM };
98*9999SWang.Lin@Sun.COM 
99*9999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_desc_dma_attr = {
100*9999SWang.Lin@Sun.COM 	DMA_ATTR_V0,	/* version number */
101*9999SWang.Lin@Sun.COM 	0,				/* low address */
102*9999SWang.Lin@Sun.COM 	0xffffffffU,	/* high address */
103*9999SWang.Lin@Sun.COM 	0xffffffffU,	/* counter register max */
104*9999SWang.Lin@Sun.COM 	0x1000,			/* alignment */
105*9999SWang.Lin@Sun.COM 	0xFFF,			/* burst sizes */
106*9999SWang.Lin@Sun.COM 	1,				/* minimum transfer size */
107*9999SWang.Lin@Sun.COM 	0xffffffffU,	/* max transfer size */
108*9999SWang.Lin@Sun.COM 	0xffffffffU,	/* address register max */
109*9999SWang.Lin@Sun.COM 	1,				/* no scatter-gather */
110*9999SWang.Lin@Sun.COM 	1,				/* granularity of device */
111*9999SWang.Lin@Sun.COM 	0,				/* DMA flags */
112*9999SWang.Lin@Sun.COM };
113*9999SWang.Lin@Sun.COM 
114*9999SWang.Lin@Sun.COM #define	ATH_DEF_CACHE_BYTES	32 /* default cache line size */
115*9999SWang.Lin@Sun.COM 
116*9999SWang.Lin@Sun.COM static kmutex_t arn_loglock;
117*9999SWang.Lin@Sun.COM static void *arn_soft_state_p = NULL;
118*9999SWang.Lin@Sun.COM /* scan interval, ms? */
119*9999SWang.Lin@Sun.COM static int arn_dwelltime = 200; /* 150 */
120*9999SWang.Lin@Sun.COM 
121*9999SWang.Lin@Sun.COM static int	arn_m_stat(void *,  uint_t, uint64_t *);
122*9999SWang.Lin@Sun.COM static int	arn_m_start(void *);
123*9999SWang.Lin@Sun.COM static void	arn_m_stop(void *);
124*9999SWang.Lin@Sun.COM static int	arn_m_promisc(void *, boolean_t);
125*9999SWang.Lin@Sun.COM static int	arn_m_multicst(void *, boolean_t, const uint8_t *);
126*9999SWang.Lin@Sun.COM static int	arn_m_unicst(void *, const uint8_t *);
127*9999SWang.Lin@Sun.COM static mblk_t	*arn_m_tx(void *, mblk_t *);
128*9999SWang.Lin@Sun.COM static void	arn_m_ioctl(void *, queue_t *, mblk_t *);
129*9999SWang.Lin@Sun.COM static int	arn_m_setprop(void *, const char *, mac_prop_id_t,
130*9999SWang.Lin@Sun.COM     uint_t, const void *);
131*9999SWang.Lin@Sun.COM static int	arn_m_getprop(void *, const char *, mac_prop_id_t,
132*9999SWang.Lin@Sun.COM     uint_t, uint_t, void *, uint_t *);
133*9999SWang.Lin@Sun.COM 
134*9999SWang.Lin@Sun.COM /* MAC Callcack Functions */
135*9999SWang.Lin@Sun.COM static mac_callbacks_t arn_m_callbacks = {
136*9999SWang.Lin@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP,
137*9999SWang.Lin@Sun.COM 	arn_m_stat,
138*9999SWang.Lin@Sun.COM 	arn_m_start,
139*9999SWang.Lin@Sun.COM 	arn_m_stop,
140*9999SWang.Lin@Sun.COM 	arn_m_promisc,
141*9999SWang.Lin@Sun.COM 	arn_m_multicst,
142*9999SWang.Lin@Sun.COM 	arn_m_unicst,
143*9999SWang.Lin@Sun.COM 	arn_m_tx,
144*9999SWang.Lin@Sun.COM 	arn_m_ioctl,
145*9999SWang.Lin@Sun.COM 	NULL,
146*9999SWang.Lin@Sun.COM 	NULL,
147*9999SWang.Lin@Sun.COM 	NULL,
148*9999SWang.Lin@Sun.COM 	arn_m_setprop,
149*9999SWang.Lin@Sun.COM 	arn_m_getprop
150*9999SWang.Lin@Sun.COM };
151*9999SWang.Lin@Sun.COM 
152*9999SWang.Lin@Sun.COM /*
153*9999SWang.Lin@Sun.COM  * ARN_DBG_HW
154*9999SWang.Lin@Sun.COM  * ARN_DBG_REG_IO
155*9999SWang.Lin@Sun.COM  * ARN_DBG_QUEUE
156*9999SWang.Lin@Sun.COM  * ARN_DBG_EEPROM
157*9999SWang.Lin@Sun.COM  * ARN_DBG_XMIT
158*9999SWang.Lin@Sun.COM  * ARN_DBG_RECV
159*9999SWang.Lin@Sun.COM  * ARN_DBG_CALIBRATE
160*9999SWang.Lin@Sun.COM  * ARN_DBG_CHANNEL
161*9999SWang.Lin@Sun.COM  * ARN_DBG_INTERRUPT
162*9999SWang.Lin@Sun.COM  * ARN_DBG_REGULATORY
163*9999SWang.Lin@Sun.COM  * ARN_DBG_ANI
164*9999SWang.Lin@Sun.COM  * ARN_DBG_POWER_MGMT
165*9999SWang.Lin@Sun.COM  * ARN_DBG_KEYCACHE
166*9999SWang.Lin@Sun.COM  * ARN_DBG_BEACON
167*9999SWang.Lin@Sun.COM  * ARN_DBG_RATE
168*9999SWang.Lin@Sun.COM  * ARN_DBG_INIT
169*9999SWang.Lin@Sun.COM  * ARN_DBG_ATTACH
170*9999SWang.Lin@Sun.COM  * ARN_DBG_DEATCH
171*9999SWang.Lin@Sun.COM  * ARN_DBG_AGGR
172*9999SWang.Lin@Sun.COM  * ARN_DBG_RESET
173*9999SWang.Lin@Sun.COM  * ARN_DBG_FATAL
174*9999SWang.Lin@Sun.COM  * ARN_DBG_ANY
175*9999SWang.Lin@Sun.COM  * ARN_DBG_ALL
176*9999SWang.Lin@Sun.COM  */
177*9999SWang.Lin@Sun.COM uint32_t arn_dbg_mask = 0;
178*9999SWang.Lin@Sun.COM 
179*9999SWang.Lin@Sun.COM /*
180*9999SWang.Lin@Sun.COM  * Exception/warning cases not leading to panic.
181*9999SWang.Lin@Sun.COM  */
182*9999SWang.Lin@Sun.COM void
183*9999SWang.Lin@Sun.COM arn_problem(const int8_t *fmt, ...)
184*9999SWang.Lin@Sun.COM {
185*9999SWang.Lin@Sun.COM 	va_list args;
186*9999SWang.Lin@Sun.COM 
187*9999SWang.Lin@Sun.COM 	mutex_enter(&arn_loglock);
188*9999SWang.Lin@Sun.COM 
189*9999SWang.Lin@Sun.COM 	va_start(args, fmt);
190*9999SWang.Lin@Sun.COM 	vcmn_err(CE_WARN, fmt, args);
191*9999SWang.Lin@Sun.COM 	va_end(args);
192*9999SWang.Lin@Sun.COM 
193*9999SWang.Lin@Sun.COM 	mutex_exit(&arn_loglock);
194*9999SWang.Lin@Sun.COM }
195*9999SWang.Lin@Sun.COM 
196*9999SWang.Lin@Sun.COM /*
197*9999SWang.Lin@Sun.COM  * Normal log information independent of debug.
198*9999SWang.Lin@Sun.COM  */
199*9999SWang.Lin@Sun.COM void
200*9999SWang.Lin@Sun.COM arn_log(const int8_t *fmt, ...)
201*9999SWang.Lin@Sun.COM {
202*9999SWang.Lin@Sun.COM 	va_list args;
203*9999SWang.Lin@Sun.COM 
204*9999SWang.Lin@Sun.COM 	mutex_enter(&arn_loglock);
205*9999SWang.Lin@Sun.COM 
206*9999SWang.Lin@Sun.COM 	va_start(args, fmt);
207*9999SWang.Lin@Sun.COM 	vcmn_err(CE_CONT, fmt, args);
208*9999SWang.Lin@Sun.COM 	va_end(args);
209*9999SWang.Lin@Sun.COM 
210*9999SWang.Lin@Sun.COM 	mutex_exit(&arn_loglock);
211*9999SWang.Lin@Sun.COM }
212*9999SWang.Lin@Sun.COM 
213*9999SWang.Lin@Sun.COM void
214*9999SWang.Lin@Sun.COM arn_dbg(uint32_t dbg_flags, const int8_t *fmt, ...)
215*9999SWang.Lin@Sun.COM {
216*9999SWang.Lin@Sun.COM 	va_list args;
217*9999SWang.Lin@Sun.COM 
218*9999SWang.Lin@Sun.COM 	if (dbg_flags & arn_dbg_mask) {
219*9999SWang.Lin@Sun.COM 		mutex_enter(&arn_loglock);
220*9999SWang.Lin@Sun.COM 		va_start(args, fmt);
221*9999SWang.Lin@Sun.COM 		vcmn_err(CE_CONT, fmt, args);
222*9999SWang.Lin@Sun.COM 		va_end(args);
223*9999SWang.Lin@Sun.COM 		mutex_exit(&arn_loglock);
224*9999SWang.Lin@Sun.COM 	}
225*9999SWang.Lin@Sun.COM }
226*9999SWang.Lin@Sun.COM 
227*9999SWang.Lin@Sun.COM /*
228*9999SWang.Lin@Sun.COM  * Read and write, they both share the same lock. We do this to serialize
229*9999SWang.Lin@Sun.COM  * reads and writes on Atheros 802.11n PCI devices only. This is required
230*9999SWang.Lin@Sun.COM  * as the FIFO on these devices can only accept sanely 2 requests. After
231*9999SWang.Lin@Sun.COM  * that the device goes bananas. Serializing the reads/writes prevents this
232*9999SWang.Lin@Sun.COM  * from happening.
233*9999SWang.Lin@Sun.COM  */
234*9999SWang.Lin@Sun.COM void
235*9999SWang.Lin@Sun.COM arn_iowrite32(struct ath_hal *ah, uint32_t reg_offset, uint32_t val)
236*9999SWang.Lin@Sun.COM {
237*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = ah->ah_sc;
238*9999SWang.Lin@Sun.COM 	if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) {
239*9999SWang.Lin@Sun.COM 		mutex_enter(&sc->sc_serial_rw);
240*9999SWang.Lin@Sun.COM 		ddi_put32(sc->sc_io_handle,
241*9999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val);
242*9999SWang.Lin@Sun.COM 		mutex_exit(&sc->sc_serial_rw);
243*9999SWang.Lin@Sun.COM 	} else {
244*9999SWang.Lin@Sun.COM 		ddi_put32(sc->sc_io_handle,
245*9999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val);
246*9999SWang.Lin@Sun.COM 	}
247*9999SWang.Lin@Sun.COM }
248*9999SWang.Lin@Sun.COM 
249*9999SWang.Lin@Sun.COM unsigned int
250*9999SWang.Lin@Sun.COM arn_ioread32(struct ath_hal *ah, uint32_t reg_offset)
251*9999SWang.Lin@Sun.COM {
252*9999SWang.Lin@Sun.COM 	uint32_t val;
253*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = ah->ah_sc;
254*9999SWang.Lin@Sun.COM 	if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) {
255*9999SWang.Lin@Sun.COM 		mutex_enter(&sc->sc_serial_rw);
256*9999SWang.Lin@Sun.COM 		val = ddi_get32(sc->sc_io_handle,
257*9999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)));
258*9999SWang.Lin@Sun.COM 		mutex_exit(&sc->sc_serial_rw);
259*9999SWang.Lin@Sun.COM 	} else {
260*9999SWang.Lin@Sun.COM 		val = ddi_get32(sc->sc_io_handle,
261*9999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)));
262*9999SWang.Lin@Sun.COM 	}
263*9999SWang.Lin@Sun.COM 
264*9999SWang.Lin@Sun.COM 	return (val);
265*9999SWang.Lin@Sun.COM }
266*9999SWang.Lin@Sun.COM 
267*9999SWang.Lin@Sun.COM void
268*9999SWang.Lin@Sun.COM arn_rx_buf_link(struct arn_softc *sc, struct ath_buf *bf)
269*9999SWang.Lin@Sun.COM {
270*9999SWang.Lin@Sun.COM 	struct ath_desc *ds;
271*9999SWang.Lin@Sun.COM 
272*9999SWang.Lin@Sun.COM 	ds = bf->bf_desc;
273*9999SWang.Lin@Sun.COM 	ds->ds_link = bf->bf_daddr;
274*9999SWang.Lin@Sun.COM 	ds->ds_data = bf->bf_dma.cookie.dmac_address;
275*9999SWang.Lin@Sun.COM 	/* virtual addr of the beginning of the buffer. */
276*9999SWang.Lin@Sun.COM 	ds->ds_vdata = bf->bf_dma.mem_va;
277*9999SWang.Lin@Sun.COM 
278*9999SWang.Lin@Sun.COM 	/*
279*9999SWang.Lin@Sun.COM 	 * setup rx descriptors. The bf_dma.alength here tells the H/W
280*9999SWang.Lin@Sun.COM 	 * how much data it can DMA to us and that we are prepared
281*9999SWang.Lin@Sun.COM 	 * to process
282*9999SWang.Lin@Sun.COM 	 */
283*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_setuprxdesc(sc->sc_ah, ds,
284*9999SWang.Lin@Sun.COM 	    bf->bf_dma.alength, /* buffer size */
285*9999SWang.Lin@Sun.COM 	    0);
286*9999SWang.Lin@Sun.COM 
287*9999SWang.Lin@Sun.COM 	if (sc->sc_rxlink != NULL)
288*9999SWang.Lin@Sun.COM 		*sc->sc_rxlink = bf->bf_daddr;
289*9999SWang.Lin@Sun.COM 	sc->sc_rxlink = &ds->ds_link;
290*9999SWang.Lin@Sun.COM }
291*9999SWang.Lin@Sun.COM 
292*9999SWang.Lin@Sun.COM /*
293*9999SWang.Lin@Sun.COM  * Allocate an area of memory and a DMA handle for accessing it
294*9999SWang.Lin@Sun.COM  */
295*9999SWang.Lin@Sun.COM static int
296*9999SWang.Lin@Sun.COM arn_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, size_t memsize,
297*9999SWang.Lin@Sun.COM     ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
298*9999SWang.Lin@Sun.COM     uint_t bind_flags, dma_area_t *dma_p)
299*9999SWang.Lin@Sun.COM {
300*9999SWang.Lin@Sun.COM 	int err;
301*9999SWang.Lin@Sun.COM 
302*9999SWang.Lin@Sun.COM 	/*
303*9999SWang.Lin@Sun.COM 	 * Allocate handle
304*9999SWang.Lin@Sun.COM 	 */
305*9999SWang.Lin@Sun.COM 	err = ddi_dma_alloc_handle(devinfo, dma_attr,
306*9999SWang.Lin@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
307*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS)
308*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
309*9999SWang.Lin@Sun.COM 
310*9999SWang.Lin@Sun.COM 	/*
311*9999SWang.Lin@Sun.COM 	 * Allocate memory
312*9999SWang.Lin@Sun.COM 	 */
313*9999SWang.Lin@Sun.COM 	err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
314*9999SWang.Lin@Sun.COM 	    alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
315*9999SWang.Lin@Sun.COM 	    &dma_p->alength, &dma_p->acc_hdl);
316*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS)
317*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
318*9999SWang.Lin@Sun.COM 
319*9999SWang.Lin@Sun.COM 	/*
320*9999SWang.Lin@Sun.COM 	 * Bind the two together
321*9999SWang.Lin@Sun.COM 	 */
322*9999SWang.Lin@Sun.COM 	err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
323*9999SWang.Lin@Sun.COM 	    dma_p->mem_va, dma_p->alength, bind_flags,
324*9999SWang.Lin@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
325*9999SWang.Lin@Sun.COM 	if (err != DDI_DMA_MAPPED)
326*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
327*9999SWang.Lin@Sun.COM 
328*9999SWang.Lin@Sun.COM 	dma_p->nslots = ~0U;
329*9999SWang.Lin@Sun.COM 	dma_p->size = ~0U;
330*9999SWang.Lin@Sun.COM 	dma_p->token = ~0U;
331*9999SWang.Lin@Sun.COM 	dma_p->offset = 0;
332*9999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
333*9999SWang.Lin@Sun.COM }
334*9999SWang.Lin@Sun.COM 
335*9999SWang.Lin@Sun.COM /*
336*9999SWang.Lin@Sun.COM  * Free one allocated area of DMAable memory
337*9999SWang.Lin@Sun.COM  */
338*9999SWang.Lin@Sun.COM static void
339*9999SWang.Lin@Sun.COM arn_free_dma_mem(dma_area_t *dma_p)
340*9999SWang.Lin@Sun.COM {
341*9999SWang.Lin@Sun.COM 	if (dma_p->dma_hdl != NULL) {
342*9999SWang.Lin@Sun.COM 		(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
343*9999SWang.Lin@Sun.COM 		if (dma_p->acc_hdl != NULL) {
344*9999SWang.Lin@Sun.COM 			ddi_dma_mem_free(&dma_p->acc_hdl);
345*9999SWang.Lin@Sun.COM 			dma_p->acc_hdl = NULL;
346*9999SWang.Lin@Sun.COM 		}
347*9999SWang.Lin@Sun.COM 		ddi_dma_free_handle(&dma_p->dma_hdl);
348*9999SWang.Lin@Sun.COM 		dma_p->ncookies = 0;
349*9999SWang.Lin@Sun.COM 		dma_p->dma_hdl = NULL;
350*9999SWang.Lin@Sun.COM 	}
351*9999SWang.Lin@Sun.COM }
352*9999SWang.Lin@Sun.COM 
353*9999SWang.Lin@Sun.COM /*
354*9999SWang.Lin@Sun.COM  * Initialize tx, rx. or beacon buffer list. Allocate DMA memory for
355*9999SWang.Lin@Sun.COM  * each buffer.
356*9999SWang.Lin@Sun.COM  */
357*9999SWang.Lin@Sun.COM static int
358*9999SWang.Lin@Sun.COM arn_buflist_setup(dev_info_t *devinfo, struct arn_softc *sc, list_t *bflist,
359*9999SWang.Lin@Sun.COM     struct ath_buf **pbf, struct ath_desc **pds, int nbuf, uint_t dmabflags)
360*9999SWang.Lin@Sun.COM {
361*9999SWang.Lin@Sun.COM 	int i, err;
362*9999SWang.Lin@Sun.COM 	struct ath_buf *bf = *pbf;
363*9999SWang.Lin@Sun.COM 	struct ath_desc *ds = *pds;
364*9999SWang.Lin@Sun.COM 
365*9999SWang.Lin@Sun.COM 	list_create(bflist, sizeof (struct ath_buf),
366*9999SWang.Lin@Sun.COM 	    offsetof(struct ath_buf, bf_node));
367*9999SWang.Lin@Sun.COM 	for (i = 0; i < nbuf; i++, bf++, ds++) {
368*9999SWang.Lin@Sun.COM 		bf->bf_desc = ds;
369*9999SWang.Lin@Sun.COM 		bf->bf_daddr = sc->sc_desc_dma.cookie.dmac_address +
370*9999SWang.Lin@Sun.COM 		    ((uintptr_t)ds - (uintptr_t)sc->sc_desc);
371*9999SWang.Lin@Sun.COM 		list_insert_tail(bflist, bf);
372*9999SWang.Lin@Sun.COM 
373*9999SWang.Lin@Sun.COM 		/* alloc DMA memory */
374*9999SWang.Lin@Sun.COM 		err = arn_alloc_dma_mem(devinfo, &arn_dma_attr,
375*9999SWang.Lin@Sun.COM 		    sc->sc_dmabuf_size, &arn_desc_accattr, DDI_DMA_STREAMING,
376*9999SWang.Lin@Sun.COM 		    dmabflags, &bf->bf_dma);
377*9999SWang.Lin@Sun.COM 		if (err != DDI_SUCCESS)
378*9999SWang.Lin@Sun.COM 			return (err);
379*9999SWang.Lin@Sun.COM 	}
380*9999SWang.Lin@Sun.COM 	*pbf = bf;
381*9999SWang.Lin@Sun.COM 	*pds = ds;
382*9999SWang.Lin@Sun.COM 
383*9999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
384*9999SWang.Lin@Sun.COM }
385*9999SWang.Lin@Sun.COM 
386*9999SWang.Lin@Sun.COM /*
387*9999SWang.Lin@Sun.COM  * Destroy tx, rx or beacon buffer list. Free DMA memory.
388*9999SWang.Lin@Sun.COM  */
389*9999SWang.Lin@Sun.COM static void
390*9999SWang.Lin@Sun.COM arn_buflist_cleanup(list_t *buflist)
391*9999SWang.Lin@Sun.COM {
392*9999SWang.Lin@Sun.COM 	struct ath_buf *bf;
393*9999SWang.Lin@Sun.COM 
394*9999SWang.Lin@Sun.COM 	if (!buflist)
395*9999SWang.Lin@Sun.COM 		return;
396*9999SWang.Lin@Sun.COM 
397*9999SWang.Lin@Sun.COM 	bf = list_head(buflist);
398*9999SWang.Lin@Sun.COM 	while (bf != NULL) {
399*9999SWang.Lin@Sun.COM 		if (bf->bf_m != NULL) {
400*9999SWang.Lin@Sun.COM 			freemsg(bf->bf_m);
401*9999SWang.Lin@Sun.COM 			bf->bf_m = NULL;
402*9999SWang.Lin@Sun.COM 		}
403*9999SWang.Lin@Sun.COM 		/* Free DMA buffer */
404*9999SWang.Lin@Sun.COM 		arn_free_dma_mem(&bf->bf_dma);
405*9999SWang.Lin@Sun.COM 		if (bf->bf_in != NULL) {
406*9999SWang.Lin@Sun.COM 			ieee80211_free_node(bf->bf_in);
407*9999SWang.Lin@Sun.COM 			bf->bf_in = NULL;
408*9999SWang.Lin@Sun.COM 		}
409*9999SWang.Lin@Sun.COM 		list_remove(buflist, bf);
410*9999SWang.Lin@Sun.COM 		bf = list_head(buflist);
411*9999SWang.Lin@Sun.COM 	}
412*9999SWang.Lin@Sun.COM 	list_destroy(buflist);
413*9999SWang.Lin@Sun.COM }
414*9999SWang.Lin@Sun.COM 
415*9999SWang.Lin@Sun.COM static void
416*9999SWang.Lin@Sun.COM arn_desc_free(struct arn_softc *sc)
417*9999SWang.Lin@Sun.COM {
418*9999SWang.Lin@Sun.COM 	arn_buflist_cleanup(&sc->sc_txbuf_list);
419*9999SWang.Lin@Sun.COM 	arn_buflist_cleanup(&sc->sc_rxbuf_list);
420*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
421*9999SWang.Lin@Sun.COM 	arn_buflist_cleanup(&sc->sc_bcbuf_list);
422*9999SWang.Lin@Sun.COM #endif
423*9999SWang.Lin@Sun.COM 
424*9999SWang.Lin@Sun.COM 	/* Free descriptor DMA buffer */
425*9999SWang.Lin@Sun.COM 	arn_free_dma_mem(&sc->sc_desc_dma);
426*9999SWang.Lin@Sun.COM 
427*9999SWang.Lin@Sun.COM 	kmem_free((void *)sc->sc_vbufptr, sc->sc_vbuflen);
428*9999SWang.Lin@Sun.COM 	sc->sc_vbufptr = NULL;
429*9999SWang.Lin@Sun.COM }
430*9999SWang.Lin@Sun.COM 
431*9999SWang.Lin@Sun.COM static int
432*9999SWang.Lin@Sun.COM arn_desc_alloc(dev_info_t *devinfo, struct arn_softc *sc)
433*9999SWang.Lin@Sun.COM {
434*9999SWang.Lin@Sun.COM 	int err;
435*9999SWang.Lin@Sun.COM 	size_t size;
436*9999SWang.Lin@Sun.COM 	struct ath_desc *ds;
437*9999SWang.Lin@Sun.COM 	struct ath_buf *bf;
438*9999SWang.Lin@Sun.COM 
439*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
440*9999SWang.Lin@Sun.COM 	size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF);
441*9999SWang.Lin@Sun.COM #else
442*9999SWang.Lin@Sun.COM 	size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF);
443*9999SWang.Lin@Sun.COM #endif
444*9999SWang.Lin@Sun.COM 
445*9999SWang.Lin@Sun.COM 	err = arn_alloc_dma_mem(devinfo, &arn_desc_dma_attr, size,
446*9999SWang.Lin@Sun.COM 	    &arn_desc_accattr, DDI_DMA_CONSISTENT,
447*9999SWang.Lin@Sun.COM 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &sc->sc_desc_dma);
448*9999SWang.Lin@Sun.COM 
449*9999SWang.Lin@Sun.COM 	/* virtual address of the first descriptor */
450*9999SWang.Lin@Sun.COM 	sc->sc_desc = (struct ath_desc *)sc->sc_desc_dma.mem_va;
451*9999SWang.Lin@Sun.COM 
452*9999SWang.Lin@Sun.COM 	ds = sc->sc_desc;
453*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: arn_desc_alloc(): DMA map: "
454*9999SWang.Lin@Sun.COM 	    "%p (%d) -> %p\n",
455*9999SWang.Lin@Sun.COM 	    sc->sc_desc, sc->sc_desc_dma.alength,
456*9999SWang.Lin@Sun.COM 	    sc->sc_desc_dma.cookie.dmac_address));
457*9999SWang.Lin@Sun.COM 
458*9999SWang.Lin@Sun.COM 	/* allocate data structures to describe TX/RX DMA buffers */
459*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
460*9999SWang.Lin@Sun.COM 	sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF +
461*9999SWang.Lin@Sun.COM 	    ATH_BCBUF);
462*9999SWang.Lin@Sun.COM #else
463*9999SWang.Lin@Sun.COM 	sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF);
464*9999SWang.Lin@Sun.COM #endif
465*9999SWang.Lin@Sun.COM 	bf = (struct ath_buf *)kmem_zalloc(sc->sc_vbuflen, KM_SLEEP);
466*9999SWang.Lin@Sun.COM 	sc->sc_vbufptr = bf;
467*9999SWang.Lin@Sun.COM 
468*9999SWang.Lin@Sun.COM 	/* DMA buffer size for each TX/RX packet */
469*9999SWang.Lin@Sun.COM 	sc->sc_dmabuf_size = roundup(1000 + sizeof (struct ieee80211_frame) +
470*9999SWang.Lin@Sun.COM 	    IEEE80211_MTU + IEEE80211_CRC_LEN +
471*9999SWang.Lin@Sun.COM 	    (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
472*9999SWang.Lin@Sun.COM 	    IEEE80211_WEP_CRCLEN), sc->sc_cachelsz);
473*9999SWang.Lin@Sun.COM 
474*9999SWang.Lin@Sun.COM 	/* create RX buffer list */
475*9999SWang.Lin@Sun.COM 	err = arn_buflist_setup(devinfo, sc, &sc->sc_rxbuf_list, &bf, &ds,
476*9999SWang.Lin@Sun.COM 	    ATH_RXBUF, DDI_DMA_READ | DDI_DMA_STREAMING);
477*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
478*9999SWang.Lin@Sun.COM 		arn_desc_free(sc);
479*9999SWang.Lin@Sun.COM 		return (err);
480*9999SWang.Lin@Sun.COM 	}
481*9999SWang.Lin@Sun.COM 
482*9999SWang.Lin@Sun.COM 	/* create TX buffer list */
483*9999SWang.Lin@Sun.COM 	err = arn_buflist_setup(devinfo, sc, &sc->sc_txbuf_list, &bf, &ds,
484*9999SWang.Lin@Sun.COM 	    ATH_TXBUF, DDI_DMA_STREAMING);
485*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
486*9999SWang.Lin@Sun.COM 		arn_desc_free(sc);
487*9999SWang.Lin@Sun.COM 		return (err);
488*9999SWang.Lin@Sun.COM 	}
489*9999SWang.Lin@Sun.COM 
490*9999SWang.Lin@Sun.COM 	/* create beacon buffer list */
491*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
492*9999SWang.Lin@Sun.COM 	err = arn_buflist_setup(devinfo, sc, &sc->sc_bcbuf_list, &bf, &ds,
493*9999SWang.Lin@Sun.COM 	    ATH_BCBUF, DDI_DMA_STREAMING);
494*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
495*9999SWang.Lin@Sun.COM 		arn_desc_free(sc);
496*9999SWang.Lin@Sun.COM 		return (err);
497*9999SWang.Lin@Sun.COM 	}
498*9999SWang.Lin@Sun.COM #endif
499*9999SWang.Lin@Sun.COM 
500*9999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
501*9999SWang.Lin@Sun.COM }
502*9999SWang.Lin@Sun.COM 
503*9999SWang.Lin@Sun.COM static struct ath_rate_table *
504*9999SWang.Lin@Sun.COM /* LINTED E_STATIC_UNUSED */
505*9999SWang.Lin@Sun.COM arn_get_ratetable(struct arn_softc *sc, uint32_t mode)
506*9999SWang.Lin@Sun.COM {
507*9999SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table = NULL;
508*9999SWang.Lin@Sun.COM 
509*9999SWang.Lin@Sun.COM 	switch (mode) {
510*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
511*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
512*9999SWang.Lin@Sun.COM 		break;
513*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
514*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11B];
515*9999SWang.Lin@Sun.COM 		break;
516*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
517*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
518*9999SWang.Lin@Sun.COM 		break;
519*9999SWang.Lin@Sun.COM #ifdef ARB_11N
520*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT20:
521*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
522*9999SWang.Lin@Sun.COM 		break;
523*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT20:
524*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
525*9999SWang.Lin@Sun.COM 		break;
526*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40PLUS:
527*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
528*9999SWang.Lin@Sun.COM 		break;
529*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40MINUS:
530*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
531*9999SWang.Lin@Sun.COM 		break;
532*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40PLUS:
533*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
534*9999SWang.Lin@Sun.COM 		break;
535*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40MINUS:
536*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
537*9999SWang.Lin@Sun.COM 		break;
538*9999SWang.Lin@Sun.COM #endif
539*9999SWang.Lin@Sun.COM 	default:
540*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: arn_get_ratetable(): "
541*9999SWang.Lin@Sun.COM 		    "invalid mode %u\n", mode));
542*9999SWang.Lin@Sun.COM 		return (NULL);
543*9999SWang.Lin@Sun.COM 	}
544*9999SWang.Lin@Sun.COM 
545*9999SWang.Lin@Sun.COM 	return (rate_table);
546*9999SWang.Lin@Sun.COM 
547*9999SWang.Lin@Sun.COM }
548*9999SWang.Lin@Sun.COM 
549*9999SWang.Lin@Sun.COM static void
550*9999SWang.Lin@Sun.COM arn_setcurmode(struct arn_softc *sc, enum wireless_mode mode)
551*9999SWang.Lin@Sun.COM {
552*9999SWang.Lin@Sun.COM 	struct ath_rate_table *rt;
553*9999SWang.Lin@Sun.COM 	int i;
554*9999SWang.Lin@Sun.COM 
555*9999SWang.Lin@Sun.COM 	for (i = 0; i < sizeof (sc->asc_rixmap); i++)
556*9999SWang.Lin@Sun.COM 		sc->asc_rixmap[i] = 0xff;
557*9999SWang.Lin@Sun.COM 
558*9999SWang.Lin@Sun.COM 	rt = sc->hw_rate_table[mode];
559*9999SWang.Lin@Sun.COM 	ASSERT(rt != NULL);
560*9999SWang.Lin@Sun.COM 
561*9999SWang.Lin@Sun.COM 	for (i = 0; i < rt->rate_cnt; i++)
562*9999SWang.Lin@Sun.COM 		sc->asc_rixmap[rt->info[i].dot11rate &
563*9999SWang.Lin@Sun.COM 		    IEEE80211_RATE_VAL] = (uint8_t)i; /* LINT */
564*9999SWang.Lin@Sun.COM 
565*9999SWang.Lin@Sun.COM 	sc->sc_currates = rt;
566*9999SWang.Lin@Sun.COM 	sc->sc_curmode = mode;
567*9999SWang.Lin@Sun.COM 
568*9999SWang.Lin@Sun.COM 	/*
569*9999SWang.Lin@Sun.COM 	 * All protection frames are transmited at 2Mb/s for
570*9999SWang.Lin@Sun.COM 	 * 11g, otherwise at 1Mb/s.
571*9999SWang.Lin@Sun.COM 	 * XXX select protection rate index from rate table.
572*9999SWang.Lin@Sun.COM 	 */
573*9999SWang.Lin@Sun.COM 	sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0);
574*9999SWang.Lin@Sun.COM }
575*9999SWang.Lin@Sun.COM 
576*9999SWang.Lin@Sun.COM static enum wireless_mode
577*9999SWang.Lin@Sun.COM arn_chan2mode(struct ath9k_channel *chan)
578*9999SWang.Lin@Sun.COM {
579*9999SWang.Lin@Sun.COM 	if (chan->chanmode == CHANNEL_A)
580*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11A);
581*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G)
582*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11G);
583*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_B)
584*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11B);
585*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_A_HT20)
586*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NA_HT20);
587*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G_HT20)
588*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NG_HT20);
589*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_A_HT40PLUS)
590*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NA_HT40PLUS);
591*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_A_HT40MINUS)
592*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NA_HT40MINUS);
593*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G_HT40PLUS)
594*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NG_HT40PLUS);
595*9999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G_HT40MINUS)
596*9999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NG_HT40MINUS);
597*9999SWang.Lin@Sun.COM 
598*9999SWang.Lin@Sun.COM 	return (ATH9K_MODE_11B);
599*9999SWang.Lin@Sun.COM }
600*9999SWang.Lin@Sun.COM 
601*9999SWang.Lin@Sun.COM static void
602*9999SWang.Lin@Sun.COM arn_update_txpow(struct arn_softc *sc)
603*9999SWang.Lin@Sun.COM {
604*9999SWang.Lin@Sun.COM 	struct ath_hal 	*ah = sc->sc_ah;
605*9999SWang.Lin@Sun.COM 	uint32_t txpow;
606*9999SWang.Lin@Sun.COM 
607*9999SWang.Lin@Sun.COM 	if (sc->sc_curtxpow != sc->sc_config.txpowlimit) {
608*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit);
609*9999SWang.Lin@Sun.COM 		/* read back in case value is clamped */
610*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
611*9999SWang.Lin@Sun.COM 		sc->sc_curtxpow = (uint32_t)txpow;
612*9999SWang.Lin@Sun.COM 	}
613*9999SWang.Lin@Sun.COM }
614*9999SWang.Lin@Sun.COM 
615*9999SWang.Lin@Sun.COM static void
616*9999SWang.Lin@Sun.COM arn_setup_rates(struct arn_softc *sc, uint32_t mode)
617*9999SWang.Lin@Sun.COM {
618*9999SWang.Lin@Sun.COM 	int i, maxrates;
619*9999SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table = NULL;
620*9999SWang.Lin@Sun.COM 	struct ieee80211_rateset *rateset;
621*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
622*9999SWang.Lin@Sun.COM 
623*9999SWang.Lin@Sun.COM 	/* rate_table = arn_get_ratetable(sc, mode); */
624*9999SWang.Lin@Sun.COM 	switch (mode) {
625*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
626*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
627*9999SWang.Lin@Sun.COM 		break;
628*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
629*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11B];
630*9999SWang.Lin@Sun.COM 		break;
631*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
632*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
633*9999SWang.Lin@Sun.COM 		break;
634*9999SWang.Lin@Sun.COM #ifdef ARN_11N
635*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT20:
636*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
637*9999SWang.Lin@Sun.COM 		break;
638*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT20:
639*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
640*9999SWang.Lin@Sun.COM 		break;
641*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40PLUS:
642*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
643*9999SWang.Lin@Sun.COM 		break;
644*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40MINUS:
645*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
646*9999SWang.Lin@Sun.COM 		break;
647*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40PLUS:
648*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
649*9999SWang.Lin@Sun.COM 		break;
650*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40MINUS:
651*9999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
652*9999SWang.Lin@Sun.COM 		break;
653*9999SWang.Lin@Sun.COM #endif
654*9999SWang.Lin@Sun.COM 	default:
655*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_get_ratetable(): "
656*9999SWang.Lin@Sun.COM 		    "invalid mode %u\n", mode));
657*9999SWang.Lin@Sun.COM 		break;
658*9999SWang.Lin@Sun.COM 	}
659*9999SWang.Lin@Sun.COM 	if (rate_table == NULL)
660*9999SWang.Lin@Sun.COM 		return;
661*9999SWang.Lin@Sun.COM 	if (rate_table->rate_cnt > ATH_RATE_MAX) {
662*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
663*9999SWang.Lin@Sun.COM 		    "rate table too small (%u > %u)\n",
664*9999SWang.Lin@Sun.COM 		    rate_table->rate_cnt, IEEE80211_RATE_MAXSIZE));
665*9999SWang.Lin@Sun.COM 		maxrates = ATH_RATE_MAX;
666*9999SWang.Lin@Sun.COM 	} else
667*9999SWang.Lin@Sun.COM 		maxrates = rate_table->rate_cnt;
668*9999SWang.Lin@Sun.COM 
669*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
670*9999SWang.Lin@Sun.COM 	    "maxrates is %d\n", maxrates));
671*9999SWang.Lin@Sun.COM 
672*9999SWang.Lin@Sun.COM 	rateset = &ic->ic_sup_rates[mode];
673*9999SWang.Lin@Sun.COM 	for (i = 0; i < maxrates; i++) {
674*9999SWang.Lin@Sun.COM 		rateset->ir_rates[i] = rate_table->info[i].dot11rate;
675*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
676*9999SWang.Lin@Sun.COM 		    "%d\n", rate_table->info[i].dot11rate));
677*9999SWang.Lin@Sun.COM 	}
678*9999SWang.Lin@Sun.COM 	rateset->ir_nrates = (uint8_t)maxrates; /* ??? */
679*9999SWang.Lin@Sun.COM }
680*9999SWang.Lin@Sun.COM 
681*9999SWang.Lin@Sun.COM static int
682*9999SWang.Lin@Sun.COM arn_setup_channels(struct arn_softc *sc)
683*9999SWang.Lin@Sun.COM {
684*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
685*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
686*9999SWang.Lin@Sun.COM 	int nchan, i, index;
687*9999SWang.Lin@Sun.COM 	uint8_t regclassids[ATH_REGCLASSIDS_MAX];
688*9999SWang.Lin@Sun.COM 	uint32_t nregclass = 0;
689*9999SWang.Lin@Sun.COM 	struct ath9k_channel *c;
690*9999SWang.Lin@Sun.COM 
691*9999SWang.Lin@Sun.COM 	/* Fill in ah->ah_channels */
692*9999SWang.Lin@Sun.COM 	if (!ath9k_regd_init_channels(ah, ATH_CHAN_MAX, (uint32_t *)&nchan,
693*9999SWang.Lin@Sun.COM 	    regclassids, ATH_REGCLASSIDS_MAX, &nregclass, CTRY_DEFAULT,
694*9999SWang.Lin@Sun.COM 	    B_FALSE, 1)) {
695*9999SWang.Lin@Sun.COM 		uint32_t rd = ah->ah_currentRD;
696*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): "
697*9999SWang.Lin@Sun.COM 		    "unable to collect channel list; "
698*9999SWang.Lin@Sun.COM 		    "regdomain likely %u country code %u\n",
699*9999SWang.Lin@Sun.COM 		    rd, CTRY_DEFAULT));
700*9999SWang.Lin@Sun.COM 		return (EINVAL);
701*9999SWang.Lin@Sun.COM 	}
702*9999SWang.Lin@Sun.COM 
703*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): "
704*9999SWang.Lin@Sun.COM 	    "number of channel is %d\n", nchan));
705*9999SWang.Lin@Sun.COM 
706*9999SWang.Lin@Sun.COM 	for (i = 0; i < nchan; i++) {
707*9999SWang.Lin@Sun.COM 		c = &ah->ah_channels[i];
708*9999SWang.Lin@Sun.COM 		uint16_t flags;
709*9999SWang.Lin@Sun.COM 		index = ath9k_hw_mhz2ieee(ah, c->channel, c->channelFlags);
710*9999SWang.Lin@Sun.COM 
711*9999SWang.Lin@Sun.COM 		if (index > IEEE80211_CHAN_MAX) {
712*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CHANNEL,
713*9999SWang.Lin@Sun.COM 			    "arn: arn_setup_channels(): "
714*9999SWang.Lin@Sun.COM 			    "bad hal channel %d (%u/%x) ignored\n",
715*9999SWang.Lin@Sun.COM 			    index, c->channel, c->channelFlags));
716*9999SWang.Lin@Sun.COM 			continue;
717*9999SWang.Lin@Sun.COM 		}
718*9999SWang.Lin@Sun.COM 		/* NB: flags are known to be compatible */
719*9999SWang.Lin@Sun.COM 		if (index < 0) {
720*9999SWang.Lin@Sun.COM 			/*
721*9999SWang.Lin@Sun.COM 			 * can't handle frequency <2400MHz (negative
722*9999SWang.Lin@Sun.COM 			 * channels) right now
723*9999SWang.Lin@Sun.COM 			 */
724*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CHANNEL,
725*9999SWang.Lin@Sun.COM 			    "arn: arn_setup_channels(): "
726*9999SWang.Lin@Sun.COM 			    "hal channel %d (%u/%x) "
727*9999SWang.Lin@Sun.COM 			    "cannot be handled, ignored\n",
728*9999SWang.Lin@Sun.COM 			    index, c->channel, c->channelFlags));
729*9999SWang.Lin@Sun.COM 			continue;
730*9999SWang.Lin@Sun.COM 		}
731*9999SWang.Lin@Sun.COM 
732*9999SWang.Lin@Sun.COM 		/*
733*9999SWang.Lin@Sun.COM 		 * Calculate net80211 flags; most are compatible
734*9999SWang.Lin@Sun.COM 		 * but some need massaging.  Note the static turbo
735*9999SWang.Lin@Sun.COM 		 * conversion can be removed once net80211 is updated
736*9999SWang.Lin@Sun.COM 		 * to understand static vs. dynamic turbo.
737*9999SWang.Lin@Sun.COM 		 */
738*9999SWang.Lin@Sun.COM 
739*9999SWang.Lin@Sun.COM 		flags = c->channelFlags & (CHANNEL_ALL | CHANNEL_PASSIVE);
740*9999SWang.Lin@Sun.COM 
741*9999SWang.Lin@Sun.COM 		if (ic->ic_sup_channels[index].ich_freq == 0) {
742*9999SWang.Lin@Sun.COM 			ic->ic_sup_channels[index].ich_freq = c->channel;
743*9999SWang.Lin@Sun.COM 			ic->ic_sup_channels[index].ich_flags = flags;
744*9999SWang.Lin@Sun.COM 		} else {
745*9999SWang.Lin@Sun.COM 			/* channels overlap; e.g. 11g and 11b */
746*9999SWang.Lin@Sun.COM 			ic->ic_sup_channels[index].ich_flags |= flags;
747*9999SWang.Lin@Sun.COM 		}
748*9999SWang.Lin@Sun.COM 		if ((c->channelFlags & CHANNEL_G) == CHANNEL_G) {
749*9999SWang.Lin@Sun.COM 			sc->sc_have11g = 1;
750*9999SWang.Lin@Sun.COM 			ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
751*9999SWang.Lin@Sun.COM 			    IEEE80211_C_SHSLOT;	/* short slot time */
752*9999SWang.Lin@Sun.COM 		}
753*9999SWang.Lin@Sun.COM 	}
754*9999SWang.Lin@Sun.COM 
755*9999SWang.Lin@Sun.COM 	return (0);
756*9999SWang.Lin@Sun.COM }
757*9999SWang.Lin@Sun.COM 
758*9999SWang.Lin@Sun.COM uint32_t
759*9999SWang.Lin@Sun.COM arn_chan2flags(ieee80211com_t *isc, struct ieee80211_channel *chan)
760*9999SWang.Lin@Sun.COM {
761*9999SWang.Lin@Sun.COM 	static const uint32_t modeflags[] = {
762*9999SWang.Lin@Sun.COM 	    0,				/* IEEE80211_MODE_AUTO */
763*9999SWang.Lin@Sun.COM 	    CHANNEL_A,			/* IEEE80211_MODE_11A */
764*9999SWang.Lin@Sun.COM 	    CHANNEL_B,			/* IEEE80211_MODE_11B */
765*9999SWang.Lin@Sun.COM 	    CHANNEL_G,		/* IEEE80211_MODE_11G */
766*9999SWang.Lin@Sun.COM 	    0,				/*  */
767*9999SWang.Lin@Sun.COM 	    0,		/*  */
768*9999SWang.Lin@Sun.COM 	    0		/*  */
769*9999SWang.Lin@Sun.COM 	};
770*9999SWang.Lin@Sun.COM 	return (modeflags[ieee80211_chan2mode(isc, chan)]);
771*9999SWang.Lin@Sun.COM }
772*9999SWang.Lin@Sun.COM 
773*9999SWang.Lin@Sun.COM /*
774*9999SWang.Lin@Sun.COM  * Update internal state after a channel change.
775*9999SWang.Lin@Sun.COM  */
776*9999SWang.Lin@Sun.COM void
777*9999SWang.Lin@Sun.COM arn_chan_change(struct arn_softc *sc, struct ieee80211_channel *chan)
778*9999SWang.Lin@Sun.COM {
779*9999SWang.Lin@Sun.COM 	struct ieee80211com *ic = &sc->sc_isc;
780*9999SWang.Lin@Sun.COM 	enum ieee80211_phymode mode;
781*9999SWang.Lin@Sun.COM 	enum wireless_mode wlmode;
782*9999SWang.Lin@Sun.COM 
783*9999SWang.Lin@Sun.COM 	/*
784*9999SWang.Lin@Sun.COM 	 * Change channels and update the h/w rate map
785*9999SWang.Lin@Sun.COM 	 * if we're switching; e.g. 11a to 11b/g.
786*9999SWang.Lin@Sun.COM 	 */
787*9999SWang.Lin@Sun.COM 	mode = ieee80211_chan2mode(ic, chan);
788*9999SWang.Lin@Sun.COM 	switch (mode) {
789*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
790*9999SWang.Lin@Sun.COM 		wlmode = ATH9K_MODE_11A;
791*9999SWang.Lin@Sun.COM 		break;
792*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
793*9999SWang.Lin@Sun.COM 		wlmode = ATH9K_MODE_11B;
794*9999SWang.Lin@Sun.COM 		break;
795*9999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
796*9999SWang.Lin@Sun.COM 		wlmode = ATH9K_MODE_11B;
797*9999SWang.Lin@Sun.COM 		break;
798*9999SWang.Lin@Sun.COM 	default:
799*9999SWang.Lin@Sun.COM 		break;
800*9999SWang.Lin@Sun.COM 	}
801*9999SWang.Lin@Sun.COM 	if (wlmode != sc->sc_curmode)
802*9999SWang.Lin@Sun.COM 		arn_setcurmode(sc, wlmode);
803*9999SWang.Lin@Sun.COM 
804*9999SWang.Lin@Sun.COM }
805*9999SWang.Lin@Sun.COM 
806*9999SWang.Lin@Sun.COM /*
807*9999SWang.Lin@Sun.COM  * Set/change channels.  If the channel is really being changed, it's done
808*9999SWang.Lin@Sun.COM  * by reseting the chip.  To accomplish this we must first cleanup any pending
809*9999SWang.Lin@Sun.COM  * DMA, then restart stuff.
810*9999SWang.Lin@Sun.COM  */
811*9999SWang.Lin@Sun.COM static int
812*9999SWang.Lin@Sun.COM arn_set_channel(struct arn_softc *sc, struct ath9k_channel *hchan)
813*9999SWang.Lin@Sun.COM {
814*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
815*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = &sc->sc_isc;
816*9999SWang.Lin@Sun.COM 	boolean_t fastcc = B_TRUE;
817*9999SWang.Lin@Sun.COM 	boolean_t  stopped;
818*9999SWang.Lin@Sun.COM 	struct ieee80211_channel chan;
819*9999SWang.Lin@Sun.COM 	enum wireless_mode curmode;
820*9999SWang.Lin@Sun.COM 
821*9999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID)
822*9999SWang.Lin@Sun.COM 		return (EIO);
823*9999SWang.Lin@Sun.COM 
824*9999SWang.Lin@Sun.COM 	if (hchan->channel != sc->sc_ah->ah_curchan->channel ||
825*9999SWang.Lin@Sun.COM 	    hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags ||
826*9999SWang.Lin@Sun.COM 	    (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) ||
827*9999SWang.Lin@Sun.COM 	    (sc->sc_flags & SC_OP_FULL_RESET)) {
828*9999SWang.Lin@Sun.COM 		int status;
829*9999SWang.Lin@Sun.COM 
830*9999SWang.Lin@Sun.COM 		/*
831*9999SWang.Lin@Sun.COM 		 * This is only performed if the channel settings have
832*9999SWang.Lin@Sun.COM 		 * actually changed.
833*9999SWang.Lin@Sun.COM 		 *
834*9999SWang.Lin@Sun.COM 		 * To switch channels clear any pending DMA operations;
835*9999SWang.Lin@Sun.COM 		 * wait long enough for the RX fifo to drain, reset the
836*9999SWang.Lin@Sun.COM 		 * hardware at the new frequency, and then re-enable
837*9999SWang.Lin@Sun.COM 		 * the relevant bits of the h/w.
838*9999SWang.Lin@Sun.COM 		 */
839*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts(ah, 0);	/* disable interrupts */
840*9999SWang.Lin@Sun.COM 		arn_draintxq(sc, B_FALSE);	/* clear pending tx frames */
841*9999SWang.Lin@Sun.COM 		stopped = arn_stoprecv(sc);	/* turn off frame recv */
842*9999SWang.Lin@Sun.COM 
843*9999SWang.Lin@Sun.COM 		/*
844*9999SWang.Lin@Sun.COM 		 * XXX: do not flush receive queue here. We don't want
845*9999SWang.Lin@Sun.COM 		 * to flush data frames already in queue because of
846*9999SWang.Lin@Sun.COM 		 * changing channel.
847*9999SWang.Lin@Sun.COM 		 */
848*9999SWang.Lin@Sun.COM 
849*9999SWang.Lin@Sun.COM 		if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
850*9999SWang.Lin@Sun.COM 			fastcc = B_FALSE;
851*9999SWang.Lin@Sun.COM 
852*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_set_channel(): "
853*9999SWang.Lin@Sun.COM 		    "(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n",
854*9999SWang.Lin@Sun.COM 		    sc->sc_ah->ah_curchan->channel,
855*9999SWang.Lin@Sun.COM 		    hchan->channel, hchan->channelFlags, sc->tx_chan_width));
856*9999SWang.Lin@Sun.COM 
857*9999SWang.Lin@Sun.COM 		if (!ath9k_hw_reset(ah, hchan, sc->tx_chan_width,
858*9999SWang.Lin@Sun.COM 		    sc->sc_tx_chainmask, sc->sc_rx_chainmask,
859*9999SWang.Lin@Sun.COM 		    sc->sc_ht_extprotspacing, fastcc, &status)) {
860*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_FATAL, "arn: arn_set_channel(): "
861*9999SWang.Lin@Sun.COM 			    "unable to reset channel %u (%uMhz) "
862*9999SWang.Lin@Sun.COM 			    "flags 0x%x hal status %u\n",
863*9999SWang.Lin@Sun.COM 			    ath9k_hw_mhz2ieee(ah, hchan->channel,
864*9999SWang.Lin@Sun.COM 			    hchan->channelFlags),
865*9999SWang.Lin@Sun.COM 			    hchan->channel, hchan->channelFlags, status));
866*9999SWang.Lin@Sun.COM 			return (EIO);
867*9999SWang.Lin@Sun.COM 		}
868*9999SWang.Lin@Sun.COM 
869*9999SWang.Lin@Sun.COM 		sc->sc_curchan = *hchan;
870*9999SWang.Lin@Sun.COM 
871*9999SWang.Lin@Sun.COM 		sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
872*9999SWang.Lin@Sun.COM 		sc->sc_flags &= ~SC_OP_FULL_RESET;
873*9999SWang.Lin@Sun.COM 
874*9999SWang.Lin@Sun.COM 		if (arn_startrecv(sc) != 0) {
875*9999SWang.Lin@Sun.COM 			arn_problem("arn: arn_set_channel(): "
876*9999SWang.Lin@Sun.COM 			    "unable to restart recv logic\n");
877*9999SWang.Lin@Sun.COM 			return (EIO);
878*9999SWang.Lin@Sun.COM 		}
879*9999SWang.Lin@Sun.COM 
880*9999SWang.Lin@Sun.COM 		chan.ich_freq = hchan->channel;
881*9999SWang.Lin@Sun.COM 		chan.ich_flags = hchan->channelFlags;
882*9999SWang.Lin@Sun.COM 		ic->ic_ibss_chan = &chan;
883*9999SWang.Lin@Sun.COM 
884*9999SWang.Lin@Sun.COM 		/*
885*9999SWang.Lin@Sun.COM 		 * Change channels and update the h/w rate map
886*9999SWang.Lin@Sun.COM 		 * if we're switching; e.g. 11a to 11b/g.
887*9999SWang.Lin@Sun.COM 		 */
888*9999SWang.Lin@Sun.COM 		curmode = arn_chan2mode(hchan);
889*9999SWang.Lin@Sun.COM 		if (curmode != sc->sc_curmode)
890*9999SWang.Lin@Sun.COM 			arn_setcurmode(sc, arn_chan2mode(hchan));
891*9999SWang.Lin@Sun.COM 
892*9999SWang.Lin@Sun.COM 		arn_update_txpow(sc);
893*9999SWang.Lin@Sun.COM 
894*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
895*9999SWang.Lin@Sun.COM 	}
896*9999SWang.Lin@Sun.COM 
897*9999SWang.Lin@Sun.COM 	return (0);
898*9999SWang.Lin@Sun.COM }
899*9999SWang.Lin@Sun.COM 
900*9999SWang.Lin@Sun.COM /*
901*9999SWang.Lin@Sun.COM  *  This routine performs the periodic noise floor calibration function
902*9999SWang.Lin@Sun.COM  *  that is used to adjust and optimize the chip performance.  This
903*9999SWang.Lin@Sun.COM  *  takes environmental changes (location, temperature) into account.
904*9999SWang.Lin@Sun.COM  *  When the task is complete, it reschedules itself depending on the
905*9999SWang.Lin@Sun.COM  *  appropriate interval that was calculated.
906*9999SWang.Lin@Sun.COM  */
907*9999SWang.Lin@Sun.COM static void
908*9999SWang.Lin@Sun.COM arn_ani_calibrate(void *arg)
909*9999SWang.Lin@Sun.COM 
910*9999SWang.Lin@Sun.COM {
911*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)arg;
912*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
913*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
914*9999SWang.Lin@Sun.COM 	boolean_t longcal = B_FALSE;
915*9999SWang.Lin@Sun.COM 	boolean_t shortcal = B_FALSE;
916*9999SWang.Lin@Sun.COM 	boolean_t aniflag = B_FALSE;
917*9999SWang.Lin@Sun.COM 	unsigned int timestamp = drv_hztousec(ddi_get_lbolt())/1000;
918*9999SWang.Lin@Sun.COM 	uint32_t cal_interval;
919*9999SWang.Lin@Sun.COM 
920*9999SWang.Lin@Sun.COM 	/*
921*9999SWang.Lin@Sun.COM 	 * don't calibrate when we're scanning.
922*9999SWang.Lin@Sun.COM 	 * we are most likely not on our home channel.
923*9999SWang.Lin@Sun.COM 	 */
924*9999SWang.Lin@Sun.COM 	if (ic->ic_state != IEEE80211_S_RUN)
925*9999SWang.Lin@Sun.COM 		goto settimer;
926*9999SWang.Lin@Sun.COM 
927*9999SWang.Lin@Sun.COM 	/* Long calibration runs independently of short calibration. */
928*9999SWang.Lin@Sun.COM 	if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) {
929*9999SWang.Lin@Sun.COM 		longcal = B_TRUE;
930*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
931*9999SWang.Lin@Sun.COM 		    "%s: longcal @%lu\n", __func__, drv_hztousec));
932*9999SWang.Lin@Sun.COM 		sc->sc_ani.sc_longcal_timer = timestamp;
933*9999SWang.Lin@Sun.COM 	}
934*9999SWang.Lin@Sun.COM 
935*9999SWang.Lin@Sun.COM 	/* Short calibration applies only while sc_caldone is FALSE */
936*9999SWang.Lin@Sun.COM 	if (!sc->sc_ani.sc_caldone) {
937*9999SWang.Lin@Sun.COM 		if ((timestamp - sc->sc_ani.sc_shortcal_timer) >=
938*9999SWang.Lin@Sun.COM 		    ATH_SHORT_CALINTERVAL) {
939*9999SWang.Lin@Sun.COM 			shortcal = B_TRUE;
940*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
941*9999SWang.Lin@Sun.COM 			    "%s: shortcal @%lu\n",
942*9999SWang.Lin@Sun.COM 			    __func__, drv_hztousec));
943*9999SWang.Lin@Sun.COM 			sc->sc_ani.sc_shortcal_timer = timestamp;
944*9999SWang.Lin@Sun.COM 			sc->sc_ani.sc_resetcal_timer = timestamp;
945*9999SWang.Lin@Sun.COM 		}
946*9999SWang.Lin@Sun.COM 	} else {
947*9999SWang.Lin@Sun.COM 		if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
948*9999SWang.Lin@Sun.COM 		    ATH_RESTART_CALINTERVAL) {
949*9999SWang.Lin@Sun.COM 			ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
950*9999SWang.Lin@Sun.COM 						&sc->sc_ani.sc_caldone);
951*9999SWang.Lin@Sun.COM 			if (sc->sc_ani.sc_caldone)
952*9999SWang.Lin@Sun.COM 				sc->sc_ani.sc_resetcal_timer = timestamp;
953*9999SWang.Lin@Sun.COM 		}
954*9999SWang.Lin@Sun.COM 	}
955*9999SWang.Lin@Sun.COM 
956*9999SWang.Lin@Sun.COM 	/* Verify whether we must check ANI */
957*9999SWang.Lin@Sun.COM 	if ((timestamp - sc->sc_ani.sc_checkani_timer) >=
958*9999SWang.Lin@Sun.COM 	    ATH_ANI_POLLINTERVAL) {
959*9999SWang.Lin@Sun.COM 		aniflag = B_TRUE;
960*9999SWang.Lin@Sun.COM 		sc->sc_ani.sc_checkani_timer = timestamp;
961*9999SWang.Lin@Sun.COM 	}
962*9999SWang.Lin@Sun.COM 
963*9999SWang.Lin@Sun.COM 	/* Skip all processing if there's nothing to do. */
964*9999SWang.Lin@Sun.COM 	if (longcal || shortcal || aniflag) {
965*9999SWang.Lin@Sun.COM 		/* Call ANI routine if necessary */
966*9999SWang.Lin@Sun.COM 		if (aniflag)
967*9999SWang.Lin@Sun.COM 			ath9k_hw_ani_monitor(ah, &sc->sc_halstats,
968*9999SWang.Lin@Sun.COM 			    ah->ah_curchan);
969*9999SWang.Lin@Sun.COM 
970*9999SWang.Lin@Sun.COM 		/* Perform calibration if necessary */
971*9999SWang.Lin@Sun.COM 		if (longcal || shortcal) {
972*9999SWang.Lin@Sun.COM 			boolean_t iscaldone = B_FALSE;
973*9999SWang.Lin@Sun.COM 
974*9999SWang.Lin@Sun.COM 			if (ath9k_hw_calibrate(ah, ah->ah_curchan,
975*9999SWang.Lin@Sun.COM 			    sc->sc_rx_chainmask, longcal, &iscaldone)) {
976*9999SWang.Lin@Sun.COM 				if (longcal)
977*9999SWang.Lin@Sun.COM 					sc->sc_ani.sc_noise_floor =
978*9999SWang.Lin@Sun.COM 					    ath9k_hw_getchan_noise(ah,
979*9999SWang.Lin@Sun.COM 					    ah->ah_curchan);
980*9999SWang.Lin@Sun.COM 
981*9999SWang.Lin@Sun.COM 				ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
982*9999SWang.Lin@Sun.COM 				    "%s: calibrate chan %u/%x nf: %d\n",
983*9999SWang.Lin@Sun.COM 				    __func__,
984*9999SWang.Lin@Sun.COM 				    ah->ah_curchan->channel,
985*9999SWang.Lin@Sun.COM 				    ah->ah_curchan->channelFlags,
986*9999SWang.Lin@Sun.COM 				    sc->sc_ani.sc_noise_floor));
987*9999SWang.Lin@Sun.COM 			} else {
988*9999SWang.Lin@Sun.COM 				ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
989*9999SWang.Lin@Sun.COM 				    "%s: calibrate chan %u/%x failed\n",
990*9999SWang.Lin@Sun.COM 				    __func__,
991*9999SWang.Lin@Sun.COM 				    ah->ah_curchan->channel,
992*9999SWang.Lin@Sun.COM 				    ah->ah_curchan->channelFlags));
993*9999SWang.Lin@Sun.COM 			}
994*9999SWang.Lin@Sun.COM 			sc->sc_ani.sc_caldone = iscaldone;
995*9999SWang.Lin@Sun.COM 		}
996*9999SWang.Lin@Sun.COM 	}
997*9999SWang.Lin@Sun.COM 
998*9999SWang.Lin@Sun.COM settimer:
999*9999SWang.Lin@Sun.COM 	/*
1000*9999SWang.Lin@Sun.COM 	 * Set timer interval based on previous results.
1001*9999SWang.Lin@Sun.COM 	 * The interval must be the shortest necessary to satisfy ANI,
1002*9999SWang.Lin@Sun.COM 	 * short calibration and long calibration.
1003*9999SWang.Lin@Sun.COM 	 */
1004*9999SWang.Lin@Sun.COM 	cal_interval = ATH_LONG_CALINTERVAL;
1005*9999SWang.Lin@Sun.COM 	if (sc->sc_ah->ah_config.enable_ani)
1006*9999SWang.Lin@Sun.COM 		cal_interval =
1007*9999SWang.Lin@Sun.COM 		    min(cal_interval, (uint32_t)ATH_ANI_POLLINTERVAL);
1008*9999SWang.Lin@Sun.COM 
1009*9999SWang.Lin@Sun.COM 	if (!sc->sc_ani.sc_caldone)
1010*9999SWang.Lin@Sun.COM 		cal_interval = min(cal_interval,
1011*9999SWang.Lin@Sun.COM 		    (uint32_t)ATH_SHORT_CALINTERVAL);
1012*9999SWang.Lin@Sun.COM 
1013*9999SWang.Lin@Sun.COM 	sc->sc_scan_timer = 0;
1014*9999SWang.Lin@Sun.COM 	sc->sc_scan_timer = timeout(arn_ani_calibrate, (void *)sc,
1015*9999SWang.Lin@Sun.COM 	    drv_usectohz(cal_interval * 1000));
1016*9999SWang.Lin@Sun.COM }
1017*9999SWang.Lin@Sun.COM 
1018*9999SWang.Lin@Sun.COM static void
1019*9999SWang.Lin@Sun.COM arn_stop_caltimer(struct arn_softc *sc)
1020*9999SWang.Lin@Sun.COM {
1021*9999SWang.Lin@Sun.COM 	timeout_id_t tmp_id = 0;
1022*9999SWang.Lin@Sun.COM 
1023*9999SWang.Lin@Sun.COM 	while ((sc->sc_cal_timer != 0) && (tmp_id != sc->sc_cal_timer)) {
1024*9999SWang.Lin@Sun.COM 		tmp_id = sc->sc_cal_timer;
1025*9999SWang.Lin@Sun.COM 		(void) untimeout(tmp_id);
1026*9999SWang.Lin@Sun.COM 	}
1027*9999SWang.Lin@Sun.COM 	sc->sc_cal_timer = 0;
1028*9999SWang.Lin@Sun.COM }
1029*9999SWang.Lin@Sun.COM 
1030*9999SWang.Lin@Sun.COM static uint_t
1031*9999SWang.Lin@Sun.COM arn_isr(caddr_t arg)
1032*9999SWang.Lin@Sun.COM {
1033*9999SWang.Lin@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
1034*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)arg;
1035*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
1036*9999SWang.Lin@Sun.COM 	enum ath9k_int status;
1037*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
1038*9999SWang.Lin@Sun.COM 
1039*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
1040*9999SWang.Lin@Sun.COM 
1041*9999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID) {
1042*9999SWang.Lin@Sun.COM 		/*
1043*9999SWang.Lin@Sun.COM 		 * The hardware is not ready/present, don't
1044*9999SWang.Lin@Sun.COM 		 * touch anything. Note this can happen early
1045*9999SWang.Lin@Sun.COM 		 * on if the IRQ is shared.
1046*9999SWang.Lin@Sun.COM 		 */
1047*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1048*9999SWang.Lin@Sun.COM 		return (DDI_INTR_UNCLAIMED);
1049*9999SWang.Lin@Sun.COM 	}
1050*9999SWang.Lin@Sun.COM 	if (!ath9k_hw_intrpend(ah)) {	/* shared irq, not for us */
1051*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1052*9999SWang.Lin@Sun.COM 		return (DDI_INTR_UNCLAIMED);
1053*9999SWang.Lin@Sun.COM 	}
1054*9999SWang.Lin@Sun.COM 
1055*9999SWang.Lin@Sun.COM 	/*
1056*9999SWang.Lin@Sun.COM 	 * Figure out the reason(s) for the interrupt. Note
1057*9999SWang.Lin@Sun.COM 	 * that the hal returns a pseudo-ISR that may include
1058*9999SWang.Lin@Sun.COM 	 * bits we haven't explicitly enabled so we mask the
1059*9999SWang.Lin@Sun.COM 	 * value to insure we only process bits we requested.
1060*9999SWang.Lin@Sun.COM 	 */
1061*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
1062*9999SWang.Lin@Sun.COM 
1063*9999SWang.Lin@Sun.COM 	status &= sc->sc_imask; /* discard unasked-for bits */
1064*9999SWang.Lin@Sun.COM 
1065*9999SWang.Lin@Sun.COM 	/*
1066*9999SWang.Lin@Sun.COM 	 * If there are no status bits set, then this interrupt was not
1067*9999SWang.Lin@Sun.COM 	 * for me (should have been caught above).
1068*9999SWang.Lin@Sun.COM 	 */
1069*9999SWang.Lin@Sun.COM 	if (!status) {
1070*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1071*9999SWang.Lin@Sun.COM 		return (DDI_INTR_UNCLAIMED);
1072*9999SWang.Lin@Sun.COM 	}
1073*9999SWang.Lin@Sun.COM 
1074*9999SWang.Lin@Sun.COM 	sc->sc_intrstatus = status;
1075*9999SWang.Lin@Sun.COM 
1076*9999SWang.Lin@Sun.COM 	if (status & ATH9K_INT_FATAL) {
1077*9999SWang.Lin@Sun.COM 		/* need a chip reset */
1078*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1079*9999SWang.Lin@Sun.COM 		    "ATH9K_INT_FATAL\n"));
1080*9999SWang.Lin@Sun.COM 		goto reset;
1081*9999SWang.Lin@Sun.COM 	} else if (status & ATH9K_INT_RXORN) {
1082*9999SWang.Lin@Sun.COM 		/* need a chip reset */
1083*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1084*9999SWang.Lin@Sun.COM 		    "ATH9K_INT_RXORN\n"));
1085*9999SWang.Lin@Sun.COM 		goto reset;
1086*9999SWang.Lin@Sun.COM 	} else {
1087*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_RXEOL) {
1088*9999SWang.Lin@Sun.COM 			/*
1089*9999SWang.Lin@Sun.COM 			 * NB: the hardware should re-read the link when
1090*9999SWang.Lin@Sun.COM 			 * RXE bit is written, but it doesn't work
1091*9999SWang.Lin@Sun.COM 			 * at least on older hardware revs.
1092*9999SWang.Lin@Sun.COM 			 */
1093*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1094*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_RXEOL\n"));
1095*9999SWang.Lin@Sun.COM 			sc->sc_rxlink = NULL;
1096*9999SWang.Lin@Sun.COM 		}
1097*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_TXURN) {
1098*9999SWang.Lin@Sun.COM 			/* bump tx trigger level */
1099*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1100*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_TXURN\n"));
1101*9999SWang.Lin@Sun.COM 			(void) ath9k_hw_updatetxtriglevel(ah, B_TRUE);
1102*9999SWang.Lin@Sun.COM 		}
1103*9999SWang.Lin@Sun.COM 		/* XXX: optimize this */
1104*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_RX) {
1105*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1106*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_RX\n"));
1107*9999SWang.Lin@Sun.COM 			sc->sc_rx_pend = 1;
1108*9999SWang.Lin@Sun.COM 			ddi_trigger_softintr(sc->sc_softint_id);
1109*9999SWang.Lin@Sun.COM 		}
1110*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_TX) {
1111*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1112*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_TX\n"));
1113*9999SWang.Lin@Sun.COM 			if (ddi_taskq_dispatch(sc->sc_tq,
1114*9999SWang.Lin@Sun.COM 			    arn_tx_int_proc, sc, DDI_NOSLEEP) !=
1115*9999SWang.Lin@Sun.COM 			    DDI_SUCCESS) {
1116*9999SWang.Lin@Sun.COM 				arn_problem("arn: arn_isr(): "
1117*9999SWang.Lin@Sun.COM 				    "No memory for tx taskq\n");
1118*9999SWang.Lin@Sun.COM 				}
1119*9999SWang.Lin@Sun.COM 			}
1120*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB
1121*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_MIB) {
1122*9999SWang.Lin@Sun.COM 			/*
1123*9999SWang.Lin@Sun.COM 			 * Disable interrupts until we service the MIB
1124*9999SWang.Lin@Sun.COM 			 * interrupt; otherwise it will continue to
1125*9999SWang.Lin@Sun.COM 			 * fire.
1126*9999SWang.Lin@Sun.COM 			 */
1127*9999SWang.Lin@Sun.COM 			(void) ath9k_hw_set_interrupts(ah, 0);
1128*9999SWang.Lin@Sun.COM 			/*
1129*9999SWang.Lin@Sun.COM 			 * Let the hal handle the event. We assume
1130*9999SWang.Lin@Sun.COM 			 * it will clear whatever condition caused
1131*9999SWang.Lin@Sun.COM 			 * the interrupt.
1132*9999SWang.Lin@Sun.COM 			 */
1133*9999SWang.Lin@Sun.COM 			ath9k_hw_procmibevent(ah, &sc->sc_halstats);
1134*9999SWang.Lin@Sun.COM 			(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
1135*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1136*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_MIB\n"));
1137*9999SWang.Lin@Sun.COM 		}
1138*9999SWang.Lin@Sun.COM #endif
1139*9999SWang.Lin@Sun.COM 
1140*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM_TIMER
1141*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_TIM_TIMER) {
1142*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1143*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_TIM_TIMER\n"));
1144*9999SWang.Lin@Sun.COM 			if (!(ah->ah_caps.hw_caps &
1145*9999SWang.Lin@Sun.COM 			    ATH9K_HW_CAP_AUTOSLEEP)) {
1146*9999SWang.Lin@Sun.COM 				/*
1147*9999SWang.Lin@Sun.COM 				 * Clear RxAbort bit so that we can
1148*9999SWang.Lin@Sun.COM 				 * receive frames
1149*9999SWang.Lin@Sun.COM 				 */
1150*9999SWang.Lin@Sun.COM 				ath9k_hw_setrxabort(ah, 0);
1151*9999SWang.Lin@Sun.COM 				goto reset;
1152*9999SWang.Lin@Sun.COM 			}
1153*9999SWang.Lin@Sun.COM 		}
1154*9999SWang.Lin@Sun.COM #endif
1155*9999SWang.Lin@Sun.COM 
1156*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_BMISS) {
1157*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1158*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_BMISS\n"));
1159*9999SWang.Lin@Sun.COM 
1160*9999SWang.Lin@Sun.COM 			if (ddi_taskq_dispatch(sc->sc_tq, arn_bmiss_proc,
1161*9999SWang.Lin@Sun.COM 			    sc, DDI_NOSLEEP) != DDI_SUCCESS) {
1162*9999SWang.Lin@Sun.COM 				arn_problem("arn: arn_isr(): "
1163*9999SWang.Lin@Sun.COM 				    "No memory available for bmiss taskq\n");
1164*9999SWang.Lin@Sun.COM 			}
1165*9999SWang.Lin@Sun.COM 		}
1166*9999SWang.Lin@Sun.COM 
1167*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1168*9999SWang.Lin@Sun.COM 
1169*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_CST
1170*9999SWang.Lin@Sun.COM 		/* carrier sense timeout */
1171*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_CST) {
1172*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1173*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_CST\n"));
1174*9999SWang.Lin@Sun.COM 			return (DDI_INTR_CLAIMED);
1175*9999SWang.Lin@Sun.COM 		}
1176*9999SWang.Lin@Sun.COM #endif
1177*9999SWang.Lin@Sun.COM 
1178*9999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_SWBA) {
1179*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
1180*9999SWang.Lin@Sun.COM 			    "ATH9K_INT_SWBA\n"));
1181*9999SWang.Lin@Sun.COM 			/* This will occur only in Host-AP or Ad-Hoc mode */
1182*9999SWang.Lin@Sun.COM 			return (DDI_INTR_CLAIMED);
1183*9999SWang.Lin@Sun.COM 		}
1184*9999SWang.Lin@Sun.COM 	}
1185*9999SWang.Lin@Sun.COM 
1186*9999SWang.Lin@Sun.COM 	return (DDI_INTR_CLAIMED);
1187*9999SWang.Lin@Sun.COM reset:
1188*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INTERRUPT, "Rset for fatal err\n"));
1189*9999SWang.Lin@Sun.COM 	(void) arn_reset(ic);
1190*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
1191*9999SWang.Lin@Sun.COM 	return (DDI_INTR_CLAIMED);
1192*9999SWang.Lin@Sun.COM }
1193*9999SWang.Lin@Sun.COM 
1194*9999SWang.Lin@Sun.COM static int
1195*9999SWang.Lin@Sun.COM arn_get_channel(struct arn_softc *sc, struct ieee80211_channel *chan)
1196*9999SWang.Lin@Sun.COM {
1197*9999SWang.Lin@Sun.COM 	int i;
1198*9999SWang.Lin@Sun.COM 
1199*9999SWang.Lin@Sun.COM 	for (i = 0; i < sc->sc_ah->ah_nchan; i++) {
1200*9999SWang.Lin@Sun.COM 		if (sc->sc_ah->ah_channels[i].channel == chan->ich_freq)
1201*9999SWang.Lin@Sun.COM 			return (i);
1202*9999SWang.Lin@Sun.COM 	}
1203*9999SWang.Lin@Sun.COM 
1204*9999SWang.Lin@Sun.COM 	return (-1);
1205*9999SWang.Lin@Sun.COM }
1206*9999SWang.Lin@Sun.COM 
1207*9999SWang.Lin@Sun.COM int
1208*9999SWang.Lin@Sun.COM arn_reset(ieee80211com_t *ic)
1209*9999SWang.Lin@Sun.COM {
1210*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1211*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
1212*9999SWang.Lin@Sun.COM 	int status;
1213*9999SWang.Lin@Sun.COM 	int error = 0;
1214*9999SWang.Lin@Sun.COM 
1215*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, 0);
1216*9999SWang.Lin@Sun.COM 	arn_draintxq(sc, 0);
1217*9999SWang.Lin@Sun.COM 	(void) arn_stoprecv(sc);
1218*9999SWang.Lin@Sun.COM 
1219*9999SWang.Lin@Sun.COM 	if (!ath9k_hw_reset(ah, sc->sc_ah->ah_curchan, sc->tx_chan_width,
1220*9999SWang.Lin@Sun.COM 	    sc->sc_tx_chainmask, sc->sc_rx_chainmask,
1221*9999SWang.Lin@Sun.COM 	    sc->sc_ht_extprotspacing, B_FALSE, &status)) {
1222*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): "
1223*9999SWang.Lin@Sun.COM 		    "unable to reset hardware; hal status %u\n", status));
1224*9999SWang.Lin@Sun.COM 		error = EIO;
1225*9999SWang.Lin@Sun.COM 	}
1226*9999SWang.Lin@Sun.COM 
1227*9999SWang.Lin@Sun.COM 	if (arn_startrecv(sc) != 0)
1228*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): "
1229*9999SWang.Lin@Sun.COM 		    "unable to start recv logic\n"));
1230*9999SWang.Lin@Sun.COM 
1231*9999SWang.Lin@Sun.COM 	/*
1232*9999SWang.Lin@Sun.COM 	 * We may be doing a reset in response to a request
1233*9999SWang.Lin@Sun.COM 	 * that changes the channel so update any state that
1234*9999SWang.Lin@Sun.COM 	 * might change as a result.
1235*9999SWang.Lin@Sun.COM 	 */
1236*9999SWang.Lin@Sun.COM 	arn_setcurmode(sc, arn_chan2mode(sc->sc_ah->ah_curchan));
1237*9999SWang.Lin@Sun.COM 
1238*9999SWang.Lin@Sun.COM 	arn_update_txpow(sc);
1239*9999SWang.Lin@Sun.COM 
1240*9999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_BEACONS)
1241*9999SWang.Lin@Sun.COM 		arn_beacon_config(sc);	/* restart beacons */
1242*9999SWang.Lin@Sun.COM 
1243*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
1244*9999SWang.Lin@Sun.COM 
1245*9999SWang.Lin@Sun.COM 	return (error);
1246*9999SWang.Lin@Sun.COM }
1247*9999SWang.Lin@Sun.COM 
1248*9999SWang.Lin@Sun.COM int
1249*9999SWang.Lin@Sun.COM arn_get_hal_qnum(uint16_t queue, struct arn_softc *sc)
1250*9999SWang.Lin@Sun.COM {
1251*9999SWang.Lin@Sun.COM 	int qnum;
1252*9999SWang.Lin@Sun.COM 
1253*9999SWang.Lin@Sun.COM 	switch (queue) {
1254*9999SWang.Lin@Sun.COM 	case WME_AC_VO:
1255*9999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO];
1256*9999SWang.Lin@Sun.COM 		break;
1257*9999SWang.Lin@Sun.COM 	case WME_AC_VI:
1258*9999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI];
1259*9999SWang.Lin@Sun.COM 		break;
1260*9999SWang.Lin@Sun.COM 	case WME_AC_BE:
1261*9999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
1262*9999SWang.Lin@Sun.COM 		break;
1263*9999SWang.Lin@Sun.COM 	case WME_AC_BK:
1264*9999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK];
1265*9999SWang.Lin@Sun.COM 		break;
1266*9999SWang.Lin@Sun.COM 	default:
1267*9999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
1268*9999SWang.Lin@Sun.COM 		break;
1269*9999SWang.Lin@Sun.COM 	}
1270*9999SWang.Lin@Sun.COM 
1271*9999SWang.Lin@Sun.COM 	return (qnum);
1272*9999SWang.Lin@Sun.COM }
1273*9999SWang.Lin@Sun.COM 
1274*9999SWang.Lin@Sun.COM static struct {
1275*9999SWang.Lin@Sun.COM 	uint32_t version;
1276*9999SWang.Lin@Sun.COM 	const char *name;
1277*9999SWang.Lin@Sun.COM } ath_mac_bb_names[] = {
1278*9999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_5416_PCI,	"5416" },
1279*9999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_5416_PCIE,	"5418" },
1280*9999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9100,		"9100" },
1281*9999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9160,		"9160" },
1282*9999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9280,		"9280" },
1283*9999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9285,		"9285" }
1284*9999SWang.Lin@Sun.COM };
1285*9999SWang.Lin@Sun.COM 
1286*9999SWang.Lin@Sun.COM static struct {
1287*9999SWang.Lin@Sun.COM 	uint16_t version;
1288*9999SWang.Lin@Sun.COM 	const char *name;
1289*9999SWang.Lin@Sun.COM } ath_rf_names[] = {
1290*9999SWang.Lin@Sun.COM 	{ 0,				"5133" },
1291*9999SWang.Lin@Sun.COM 	{ AR_RAD5133_SREV_MAJOR,	"5133" },
1292*9999SWang.Lin@Sun.COM 	{ AR_RAD5122_SREV_MAJOR,	"5122" },
1293*9999SWang.Lin@Sun.COM 	{ AR_RAD2133_SREV_MAJOR,	"2133" },
1294*9999SWang.Lin@Sun.COM 	{ AR_RAD2122_SREV_MAJOR,	"2122" }
1295*9999SWang.Lin@Sun.COM };
1296*9999SWang.Lin@Sun.COM 
1297*9999SWang.Lin@Sun.COM /*
1298*9999SWang.Lin@Sun.COM  * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
1299*9999SWang.Lin@Sun.COM  */
1300*9999SWang.Lin@Sun.COM 
1301*9999SWang.Lin@Sun.COM static const char *
1302*9999SWang.Lin@Sun.COM arn_mac_bb_name(uint32_t mac_bb_version)
1303*9999SWang.Lin@Sun.COM {
1304*9999SWang.Lin@Sun.COM 	int i;
1305*9999SWang.Lin@Sun.COM 
1306*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(ath_mac_bb_names); i++) {
1307*9999SWang.Lin@Sun.COM 		if (ath_mac_bb_names[i].version == mac_bb_version) {
1308*9999SWang.Lin@Sun.COM 			return (ath_mac_bb_names[i].name);
1309*9999SWang.Lin@Sun.COM 		}
1310*9999SWang.Lin@Sun.COM 	}
1311*9999SWang.Lin@Sun.COM 
1312*9999SWang.Lin@Sun.COM 	return ("????");
1313*9999SWang.Lin@Sun.COM }
1314*9999SWang.Lin@Sun.COM 
1315*9999SWang.Lin@Sun.COM /*
1316*9999SWang.Lin@Sun.COM  * Return the RF name. "????" is returned if the RF is unknown.
1317*9999SWang.Lin@Sun.COM  */
1318*9999SWang.Lin@Sun.COM 
1319*9999SWang.Lin@Sun.COM static const char *
1320*9999SWang.Lin@Sun.COM arn_rf_name(uint16_t rf_version)
1321*9999SWang.Lin@Sun.COM {
1322*9999SWang.Lin@Sun.COM 	int i;
1323*9999SWang.Lin@Sun.COM 
1324*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(ath_rf_names); i++) {
1325*9999SWang.Lin@Sun.COM 		if (ath_rf_names[i].version == rf_version) {
1326*9999SWang.Lin@Sun.COM 			return (ath_rf_names[i].name);
1327*9999SWang.Lin@Sun.COM 		}
1328*9999SWang.Lin@Sun.COM 	}
1329*9999SWang.Lin@Sun.COM 
1330*9999SWang.Lin@Sun.COM 	return ("????");
1331*9999SWang.Lin@Sun.COM }
1332*9999SWang.Lin@Sun.COM 
1333*9999SWang.Lin@Sun.COM static void
1334*9999SWang.Lin@Sun.COM arn_next_scan(void *arg)
1335*9999SWang.Lin@Sun.COM {
1336*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = arg;
1337*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1338*9999SWang.Lin@Sun.COM 
1339*9999SWang.Lin@Sun.COM 	sc->sc_scan_timer = 0;
1340*9999SWang.Lin@Sun.COM 	if (ic->ic_state == IEEE80211_S_SCAN) {
1341*9999SWang.Lin@Sun.COM 		sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc,
1342*9999SWang.Lin@Sun.COM 		    drv_usectohz(arn_dwelltime * 1000));
1343*9999SWang.Lin@Sun.COM 		ieee80211_next_scan(ic);
1344*9999SWang.Lin@Sun.COM 	}
1345*9999SWang.Lin@Sun.COM }
1346*9999SWang.Lin@Sun.COM 
1347*9999SWang.Lin@Sun.COM static void
1348*9999SWang.Lin@Sun.COM arn_stop_scantimer(struct arn_softc *sc)
1349*9999SWang.Lin@Sun.COM {
1350*9999SWang.Lin@Sun.COM 	timeout_id_t tmp_id = 0;
1351*9999SWang.Lin@Sun.COM 
1352*9999SWang.Lin@Sun.COM 	while ((sc->sc_scan_timer != 0) && (tmp_id != sc->sc_scan_timer)) {
1353*9999SWang.Lin@Sun.COM 		tmp_id = sc->sc_scan_timer;
1354*9999SWang.Lin@Sun.COM 		(void) untimeout(tmp_id);
1355*9999SWang.Lin@Sun.COM 	}
1356*9999SWang.Lin@Sun.COM 	sc->sc_scan_timer = 0;
1357*9999SWang.Lin@Sun.COM }
1358*9999SWang.Lin@Sun.COM 
1359*9999SWang.Lin@Sun.COM static int32_t
1360*9999SWang.Lin@Sun.COM arn_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
1361*9999SWang.Lin@Sun.COM {
1362*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1363*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
1364*9999SWang.Lin@Sun.COM 	struct ieee80211_node *in;
1365*9999SWang.Lin@Sun.COM 	int32_t i, error;
1366*9999SWang.Lin@Sun.COM 	uint8_t *bssid;
1367*9999SWang.Lin@Sun.COM 	uint32_t rfilt;
1368*9999SWang.Lin@Sun.COM 	enum ieee80211_state ostate;
1369*9999SWang.Lin@Sun.COM 	struct ath9k_channel *channel;
1370*9999SWang.Lin@Sun.COM 	int pos;
1371*9999SWang.Lin@Sun.COM 
1372*9999SWang.Lin@Sun.COM 	/* Should set up & init LED here */
1373*9999SWang.Lin@Sun.COM 
1374*9999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID)
1375*9999SWang.Lin@Sun.COM 		return (0);
1376*9999SWang.Lin@Sun.COM 
1377*9999SWang.Lin@Sun.COM 	ostate = ic->ic_state;
1378*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: arn_newstate(): "
1379*9999SWang.Lin@Sun.COM 	    "%x -> %x!\n", ostate, nstate));
1380*9999SWang.Lin@Sun.COM 
1381*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
1382*9999SWang.Lin@Sun.COM 
1383*9999SWang.Lin@Sun.COM 	if (nstate != IEEE80211_S_SCAN)
1384*9999SWang.Lin@Sun.COM 		arn_stop_scantimer(sc);
1385*9999SWang.Lin@Sun.COM 	if (nstate != IEEE80211_S_RUN)
1386*9999SWang.Lin@Sun.COM 		arn_stop_caltimer(sc);
1387*9999SWang.Lin@Sun.COM 
1388*9999SWang.Lin@Sun.COM 	/* Should set LED here */
1389*9999SWang.Lin@Sun.COM 
1390*9999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_INIT) {
1391*9999SWang.Lin@Sun.COM 		sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
1392*9999SWang.Lin@Sun.COM 		/*
1393*9999SWang.Lin@Sun.COM 		 * Disable interrupts.
1394*9999SWang.Lin@Sun.COM 		 */
1395*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts
1396*9999SWang.Lin@Sun.COM 		    (ah, sc->sc_imask &~ ATH9K_INT_GLOBAL);
1397*9999SWang.Lin@Sun.COM 
1398*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
1399*9999SWang.Lin@Sun.COM 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
1400*9999SWang.Lin@Sun.COM 			(void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq);
1401*9999SWang.Lin@Sun.COM 			arn_beacon_return(sc);
1402*9999SWang.Lin@Sun.COM 		}
1403*9999SWang.Lin@Sun.COM #endif
1404*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1405*9999SWang.Lin@Sun.COM 		ieee80211_stop_watchdog(ic);
1406*9999SWang.Lin@Sun.COM 		goto done;
1407*9999SWang.Lin@Sun.COM 	}
1408*9999SWang.Lin@Sun.COM 	in = ic->ic_bss;
1409*9999SWang.Lin@Sun.COM 
1410*9999SWang.Lin@Sun.COM 	pos = arn_get_channel(sc, ic->ic_curchan);
1411*9999SWang.Lin@Sun.COM 
1412*9999SWang.Lin@Sun.COM 	if (pos == -1) {
1413*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: "
1414*9999SWang.Lin@Sun.COM 		    "%s: Invalid channel\n", __func__));
1415*9999SWang.Lin@Sun.COM 		error = EINVAL;
1416*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1417*9999SWang.Lin@Sun.COM 		goto bad;
1418*9999SWang.Lin@Sun.COM 	}
1419*9999SWang.Lin@Sun.COM 	sc->tx_chan_width = ATH9K_HT_MACMODE_20;
1420*9999SWang.Lin@Sun.COM 	sc->sc_ah->ah_channels[pos].chanmode =
1421*9999SWang.Lin@Sun.COM 	    arn_chan2flags(ic, ic->ic_curchan);
1422*9999SWang.Lin@Sun.COM 	channel = &sc->sc_ah->ah_channels[pos];
1423*9999SWang.Lin@Sun.COM 	if (channel == NULL) {
1424*9999SWang.Lin@Sun.COM 		arn_problem("arn_newstate(): channel == NULL");
1425*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1426*9999SWang.Lin@Sun.COM 		goto bad;
1427*9999SWang.Lin@Sun.COM 	}
1428*9999SWang.Lin@Sun.COM 	error = arn_set_channel(sc, channel);
1429*9999SWang.Lin@Sun.COM 	if (error != 0) {
1430*9999SWang.Lin@Sun.COM 		if (nstate != IEEE80211_S_SCAN) {
1431*9999SWang.Lin@Sun.COM 			ARN_UNLOCK(sc);
1432*9999SWang.Lin@Sun.COM 			ieee80211_reset_chan(ic);
1433*9999SWang.Lin@Sun.COM 			goto bad;
1434*9999SWang.Lin@Sun.COM 		}
1435*9999SWang.Lin@Sun.COM 	}
1436*9999SWang.Lin@Sun.COM 
1437*9999SWang.Lin@Sun.COM 	/*
1438*9999SWang.Lin@Sun.COM 	 * Get the receive filter according to the
1439*9999SWang.Lin@Sun.COM 	 * operating mode and state
1440*9999SWang.Lin@Sun.COM 	 */
1441*9999SWang.Lin@Sun.COM 	rfilt = arn_calcrxfilter(sc);
1442*9999SWang.Lin@Sun.COM 
1443*9999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_SCAN)
1444*9999SWang.Lin@Sun.COM 		bssid = ic->ic_macaddr;
1445*9999SWang.Lin@Sun.COM 	else
1446*9999SWang.Lin@Sun.COM 		bssid = in->in_bssid;
1447*9999SWang.Lin@Sun.COM 
1448*9999SWang.Lin@Sun.COM 	ath9k_hw_setrxfilter(ah, rfilt);
1449*9999SWang.Lin@Sun.COM 
1450*9999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS)
1451*9999SWang.Lin@Sun.COM 		ath9k_hw_write_associd(ah, bssid, in->in_associd);
1452*9999SWang.Lin@Sun.COM 	else
1453*9999SWang.Lin@Sun.COM 		ath9k_hw_write_associd(ah, bssid, 0);
1454*9999SWang.Lin@Sun.COM 
1455*9999SWang.Lin@Sun.COM 	/* Check for WLAN_CAPABILITY_PRIVACY ? */
1456*9999SWang.Lin@Sun.COM 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
1457*9999SWang.Lin@Sun.COM 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
1458*9999SWang.Lin@Sun.COM 			if (ath9k_hw_keyisvalid(ah, (uint16_t)i))
1459*9999SWang.Lin@Sun.COM 				(void) ath9k_hw_keysetmac(ah, (uint16_t)i,
1460*9999SWang.Lin@Sun.COM 				    bssid);
1461*9999SWang.Lin@Sun.COM 		}
1462*9999SWang.Lin@Sun.COM 	}
1463*9999SWang.Lin@Sun.COM 
1464*9999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_RUN) {
1465*9999SWang.Lin@Sun.COM 		switch (ic->ic_opmode) {
1466*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
1467*9999SWang.Lin@Sun.COM 		case IEEE80211_M_IBSS:
1468*9999SWang.Lin@Sun.COM 			/*
1469*9999SWang.Lin@Sun.COM 			 * Allocate and setup the beacon frame.
1470*9999SWang.Lin@Sun.COM 			 * Stop any previous beacon DMA.
1471*9999SWang.Lin@Sun.COM 			 */
1472*9999SWang.Lin@Sun.COM 			(void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq);
1473*9999SWang.Lin@Sun.COM 			arn_beacon_return(sc);
1474*9999SWang.Lin@Sun.COM 			error = arn_beacon_alloc(sc, in);
1475*9999SWang.Lin@Sun.COM 			if (error != 0) {
1476*9999SWang.Lin@Sun.COM 				ARN_UNLOCK(sc);
1477*9999SWang.Lin@Sun.COM 				goto bad;
1478*9999SWang.Lin@Sun.COM 			}
1479*9999SWang.Lin@Sun.COM 			/*
1480*9999SWang.Lin@Sun.COM 			 * If joining an adhoc network defer beacon timer
1481*9999SWang.Lin@Sun.COM 			 * configuration to the next beacon frame so we
1482*9999SWang.Lin@Sun.COM 			 * have a current TSF to use.  Otherwise we're
1483*9999SWang.Lin@Sun.COM 			 * starting an ibss/bss so there's no need to delay.
1484*9999SWang.Lin@Sun.COM 			 */
1485*9999SWang.Lin@Sun.COM 			if (ic->ic_opmode == IEEE80211_M_IBSS &&
1486*9999SWang.Lin@Sun.COM 			    ic->ic_bss->in_tstamp.tsf != 0) {
1487*9999SWang.Lin@Sun.COM 				sc->sc_bsync = 1;
1488*9999SWang.Lin@Sun.COM 			} else {
1489*9999SWang.Lin@Sun.COM 				arn_beacon_config(sc);
1490*9999SWang.Lin@Sun.COM 			}
1491*9999SWang.Lin@Sun.COM 			break;
1492*9999SWang.Lin@Sun.COM #endif /* ARN_IBSS */
1493*9999SWang.Lin@Sun.COM 		case IEEE80211_M_STA:
1494*9999SWang.Lin@Sun.COM 			if (ostate != IEEE80211_S_RUN) {
1495*9999SWang.Lin@Sun.COM 				/*
1496*9999SWang.Lin@Sun.COM 				 * Defer beacon timer configuration to the next
1497*9999SWang.Lin@Sun.COM 				 * beacon frame so we have a current TSF to use.
1498*9999SWang.Lin@Sun.COM 				 * Any TSF collected when scanning is likely old
1499*9999SWang.Lin@Sun.COM 				 */
1500*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
1501*9999SWang.Lin@Sun.COM 				sc->sc_bsync = 1;
1502*9999SWang.Lin@Sun.COM #else
1503*9999SWang.Lin@Sun.COM 				/* Configure the beacon and sleep timers. */
1504*9999SWang.Lin@Sun.COM 				arn_beacon_config(sc);
1505*9999SWang.Lin@Sun.COM #endif /* ARN_IBSS */
1506*9999SWang.Lin@Sun.COM 			}
1507*9999SWang.Lin@Sun.COM 			break;
1508*9999SWang.Lin@Sun.COM 		default:
1509*9999SWang.Lin@Sun.COM 			break;
1510*9999SWang.Lin@Sun.COM 		}
1511*9999SWang.Lin@Sun.COM 	} else {
1512*9999SWang.Lin@Sun.COM 		sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
1513*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
1514*9999SWang.Lin@Sun.COM 	}
1515*9999SWang.Lin@Sun.COM 
1516*9999SWang.Lin@Sun.COM 	/*
1517*9999SWang.Lin@Sun.COM 	 * Reset the rate control state.
1518*9999SWang.Lin@Sun.COM 	 */
1519*9999SWang.Lin@Sun.COM 	arn_rate_ctl_reset(sc, nstate);
1520*9999SWang.Lin@Sun.COM 
1521*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
1522*9999SWang.Lin@Sun.COM done:
1523*9999SWang.Lin@Sun.COM 	/*
1524*9999SWang.Lin@Sun.COM 	 * Invoke the parent method to complete the work.
1525*9999SWang.Lin@Sun.COM 	 */
1526*9999SWang.Lin@Sun.COM 	error = sc->sc_newstate(ic, nstate, arg);
1527*9999SWang.Lin@Sun.COM 
1528*9999SWang.Lin@Sun.COM 	/*
1529*9999SWang.Lin@Sun.COM 	 * Finally, start any timers.
1530*9999SWang.Lin@Sun.COM 	 */
1531*9999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_RUN) {
1532*9999SWang.Lin@Sun.COM 		ieee80211_start_watchdog(ic, 1);
1533*9999SWang.Lin@Sun.COM 		ASSERT(sc->sc_cal_timer == 0);
1534*9999SWang.Lin@Sun.COM 		sc->sc_cal_timer = timeout(arn_ani_calibrate, (void *)sc,
1535*9999SWang.Lin@Sun.COM 		    drv_usectohz(100 * 1000));
1536*9999SWang.Lin@Sun.COM 	} else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) {
1537*9999SWang.Lin@Sun.COM 		/* start ap/neighbor scan timer */
1538*9999SWang.Lin@Sun.COM 		/* ASSERT(sc->sc_scan_timer == 0); */
1539*9999SWang.Lin@Sun.COM 		if (sc->sc_scan_timer != 0) {
1540*9999SWang.Lin@Sun.COM 			(void) untimeout(sc->sc_scan_timer);
1541*9999SWang.Lin@Sun.COM 			sc->sc_scan_timer = 0;
1542*9999SWang.Lin@Sun.COM 		}
1543*9999SWang.Lin@Sun.COM 		sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc,
1544*9999SWang.Lin@Sun.COM 		    drv_usectohz(arn_dwelltime * 1000));
1545*9999SWang.Lin@Sun.COM 	}
1546*9999SWang.Lin@Sun.COM 
1547*9999SWang.Lin@Sun.COM bad:
1548*9999SWang.Lin@Sun.COM 	return (error);
1549*9999SWang.Lin@Sun.COM }
1550*9999SWang.Lin@Sun.COM 
1551*9999SWang.Lin@Sun.COM static void
1552*9999SWang.Lin@Sun.COM arn_watchdog(void *arg)
1553*9999SWang.Lin@Sun.COM {
1554*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
1555*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = &sc->sc_isc;
1556*9999SWang.Lin@Sun.COM 	int ntimer = 0;
1557*9999SWang.Lin@Sun.COM 
1558*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
1559*9999SWang.Lin@Sun.COM 	ic->ic_watchdog_timer = 0;
1560*9999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID) {
1561*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
1562*9999SWang.Lin@Sun.COM 		return;
1563*9999SWang.Lin@Sun.COM 	}
1564*9999SWang.Lin@Sun.COM 
1565*9999SWang.Lin@Sun.COM 	if (ic->ic_state == IEEE80211_S_RUN) {
1566*9999SWang.Lin@Sun.COM 		/*
1567*9999SWang.Lin@Sun.COM 		 * Start the background rate control thread if we
1568*9999SWang.Lin@Sun.COM 		 * are not configured to use a fixed xmit rate.
1569*9999SWang.Lin@Sun.COM 		 */
1570*9999SWang.Lin@Sun.COM 		if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
1571*9999SWang.Lin@Sun.COM 			sc->sc_stats.ast_rate_calls ++;
1572*9999SWang.Lin@Sun.COM 			if (ic->ic_opmode == IEEE80211_M_STA)
1573*9999SWang.Lin@Sun.COM 				arn_rate_ctl(ic, ic->ic_bss);
1574*9999SWang.Lin@Sun.COM 			else
1575*9999SWang.Lin@Sun.COM 				ieee80211_iterate_nodes(&ic->ic_sta,
1576*9999SWang.Lin@Sun.COM 				    arn_rate_ctl, sc);
1577*9999SWang.Lin@Sun.COM 		}
1578*9999SWang.Lin@Sun.COM 
1579*9999SWang.Lin@Sun.COM 		ntimer = 1;
1580*9999SWang.Lin@Sun.COM 	}
1581*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
1582*9999SWang.Lin@Sun.COM 
1583*9999SWang.Lin@Sun.COM 	ieee80211_watchdog(ic);
1584*9999SWang.Lin@Sun.COM 	if (ntimer != 0)
1585*9999SWang.Lin@Sun.COM 		ieee80211_start_watchdog(ic, ntimer);
1586*9999SWang.Lin@Sun.COM }
1587*9999SWang.Lin@Sun.COM 
1588*9999SWang.Lin@Sun.COM static struct ieee80211_node *
1589*9999SWang.Lin@Sun.COM arn_node_alloc(ieee80211com_t *ic)
1590*9999SWang.Lin@Sun.COM {
1591*9999SWang.Lin@Sun.COM 	struct ath_node *an;
1592*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1593*9999SWang.Lin@Sun.COM 
1594*9999SWang.Lin@Sun.COM 	an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP);
1595*9999SWang.Lin@Sun.COM 	arn_rate_update(sc, &an->an_node, 0);
1596*9999SWang.Lin@Sun.COM 
1597*9999SWang.Lin@Sun.COM 	return ((an != NULL) ? &an->an_node : NULL);
1598*9999SWang.Lin@Sun.COM }
1599*9999SWang.Lin@Sun.COM 
1600*9999SWang.Lin@Sun.COM static void
1601*9999SWang.Lin@Sun.COM arn_node_free(struct ieee80211_node *in)
1602*9999SWang.Lin@Sun.COM {
1603*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = in->in_ic;
1604*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1605*9999SWang.Lin@Sun.COM 	struct ath_buf *bf;
1606*9999SWang.Lin@Sun.COM 	struct ath_txq *txq;
1607*9999SWang.Lin@Sun.COM 	int32_t i;
1608*9999SWang.Lin@Sun.COM 
1609*9999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
1610*9999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i)) {
1611*9999SWang.Lin@Sun.COM 			txq = &sc->sc_txq[i];
1612*9999SWang.Lin@Sun.COM 			mutex_enter(&txq->axq_lock);
1613*9999SWang.Lin@Sun.COM 			bf = list_head(&txq->axq_list);
1614*9999SWang.Lin@Sun.COM 			while (bf != NULL) {
1615*9999SWang.Lin@Sun.COM 				if (bf->bf_in == in) {
1616*9999SWang.Lin@Sun.COM 					bf->bf_in = NULL;
1617*9999SWang.Lin@Sun.COM 				}
1618*9999SWang.Lin@Sun.COM 				bf = list_next(&txq->axq_list, bf);
1619*9999SWang.Lin@Sun.COM 			}
1620*9999SWang.Lin@Sun.COM 			mutex_exit(&txq->axq_lock);
1621*9999SWang.Lin@Sun.COM 		}
1622*9999SWang.Lin@Sun.COM 	}
1623*9999SWang.Lin@Sun.COM 
1624*9999SWang.Lin@Sun.COM 	ic->ic_node_cleanup(in);
1625*9999SWang.Lin@Sun.COM 	if (in->in_wpa_ie != NULL)
1626*9999SWang.Lin@Sun.COM 		ieee80211_free(in->in_wpa_ie);
1627*9999SWang.Lin@Sun.COM 	kmem_free(in, sizeof (struct ath_node));
1628*9999SWang.Lin@Sun.COM }
1629*9999SWang.Lin@Sun.COM 
1630*9999SWang.Lin@Sun.COM /*
1631*9999SWang.Lin@Sun.COM  * Allocate tx/rx key slots for TKIP.  We allocate one slot for
1632*9999SWang.Lin@Sun.COM  * each key. MIC is right after the decrypt/encrypt key.
1633*9999SWang.Lin@Sun.COM  */
1634*9999SWang.Lin@Sun.COM static uint16_t
1635*9999SWang.Lin@Sun.COM arn_key_alloc_pair(struct arn_softc *sc, ieee80211_keyix *txkeyix,
1636*9999SWang.Lin@Sun.COM     ieee80211_keyix *rxkeyix)
1637*9999SWang.Lin@Sun.COM {
1638*9999SWang.Lin@Sun.COM 	uint16_t i, keyix;
1639*9999SWang.Lin@Sun.COM 
1640*9999SWang.Lin@Sun.COM 	ASSERT(!sc->sc_splitmic);
1641*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) {
1642*9999SWang.Lin@Sun.COM 		uint8_t b = sc->sc_keymap[i];
1643*9999SWang.Lin@Sun.COM 		if (b == 0xff)
1644*9999SWang.Lin@Sun.COM 			continue;
1645*9999SWang.Lin@Sun.COM 		for (keyix = i * NBBY; keyix < (i + 1) * NBBY;
1646*9999SWang.Lin@Sun.COM 		    keyix++, b >>= 1) {
1647*9999SWang.Lin@Sun.COM 			if ((b & 1) || is_set(keyix+64, sc->sc_keymap)) {
1648*9999SWang.Lin@Sun.COM 				/* full pair unavailable */
1649*9999SWang.Lin@Sun.COM 				continue;
1650*9999SWang.Lin@Sun.COM 			}
1651*9999SWang.Lin@Sun.COM 			set_bit(keyix, sc->sc_keymap);
1652*9999SWang.Lin@Sun.COM 			set_bit(keyix+64, sc->sc_keymap);
1653*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_KEYCACHE,
1654*9999SWang.Lin@Sun.COM 			    "arn_key_alloc_pair(): key pair %u,%u\n",
1655*9999SWang.Lin@Sun.COM 			    keyix, keyix+64));
1656*9999SWang.Lin@Sun.COM 			*txkeyix = *rxkeyix = keyix;
1657*9999SWang.Lin@Sun.COM 			return (1);
1658*9999SWang.Lin@Sun.COM 		}
1659*9999SWang.Lin@Sun.COM 	}
1660*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_pair():"
1661*9999SWang.Lin@Sun.COM 	    " out of pair space\n"));
1662*9999SWang.Lin@Sun.COM 
1663*9999SWang.Lin@Sun.COM 	return (0);
1664*9999SWang.Lin@Sun.COM }
1665*9999SWang.Lin@Sun.COM 
1666*9999SWang.Lin@Sun.COM /*
1667*9999SWang.Lin@Sun.COM  * Allocate tx/rx key slots for TKIP.  We allocate two slots for
1668*9999SWang.Lin@Sun.COM  * each key, one for decrypt/encrypt and the other for the MIC.
1669*9999SWang.Lin@Sun.COM  */
1670*9999SWang.Lin@Sun.COM static int
1671*9999SWang.Lin@Sun.COM arn_key_alloc_2pair(struct arn_softc *sc, ieee80211_keyix *txkeyix,
1672*9999SWang.Lin@Sun.COM     ieee80211_keyix *rxkeyix)
1673*9999SWang.Lin@Sun.COM {
1674*9999SWang.Lin@Sun.COM 	uint16_t i, keyix;
1675*9999SWang.Lin@Sun.COM 
1676*9999SWang.Lin@Sun.COM 	ASSERT(sc->sc_splitmic);
1677*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) {
1678*9999SWang.Lin@Sun.COM 		uint8_t b = sc->sc_keymap[i];
1679*9999SWang.Lin@Sun.COM 		if (b != 0xff) {
1680*9999SWang.Lin@Sun.COM 			/*
1681*9999SWang.Lin@Sun.COM 			 * One or more slots in this byte are free.
1682*9999SWang.Lin@Sun.COM 			 */
1683*9999SWang.Lin@Sun.COM 			keyix = i*NBBY;
1684*9999SWang.Lin@Sun.COM 			while (b & 1) {
1685*9999SWang.Lin@Sun.COM 		again:
1686*9999SWang.Lin@Sun.COM 				keyix++;
1687*9999SWang.Lin@Sun.COM 				b >>= 1;
1688*9999SWang.Lin@Sun.COM 			}
1689*9999SWang.Lin@Sun.COM 			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
1690*9999SWang.Lin@Sun.COM 			if (is_set(keyix+32, sc->sc_keymap) ||
1691*9999SWang.Lin@Sun.COM 			    is_set(keyix+64, sc->sc_keymap) ||
1692*9999SWang.Lin@Sun.COM 			    is_set(keyix+32+64, sc->sc_keymap)) {
1693*9999SWang.Lin@Sun.COM 				/* full pair unavailable */
1694*9999SWang.Lin@Sun.COM 				if (keyix == (i+1)*NBBY) {
1695*9999SWang.Lin@Sun.COM 					/* no slots were appropriate, advance */
1696*9999SWang.Lin@Sun.COM 					continue;
1697*9999SWang.Lin@Sun.COM 				}
1698*9999SWang.Lin@Sun.COM 				goto again;
1699*9999SWang.Lin@Sun.COM 			}
1700*9999SWang.Lin@Sun.COM 			set_bit(keyix, sc->sc_keymap);
1701*9999SWang.Lin@Sun.COM 			set_bit(keyix+64, sc->sc_keymap);
1702*9999SWang.Lin@Sun.COM 			set_bit(keyix+32, sc->sc_keymap);
1703*9999SWang.Lin@Sun.COM 			set_bit(keyix+32+64, sc->sc_keymap);
1704*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_KEYCACHE,
1705*9999SWang.Lin@Sun.COM 			    "arn_key_alloc_2pair(): key pair %u,%u %u,%u\n",
1706*9999SWang.Lin@Sun.COM 			    keyix, keyix+64,
1707*9999SWang.Lin@Sun.COM 			    keyix+32, keyix+32+64));
1708*9999SWang.Lin@Sun.COM 			*txkeyix = *rxkeyix = keyix;
1709*9999SWang.Lin@Sun.COM 			return (1);
1710*9999SWang.Lin@Sun.COM 		}
1711*9999SWang.Lin@Sun.COM 	}
1712*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_2pair(): "
1713*9999SWang.Lin@Sun.COM 	    " out of pair space\n"));
1714*9999SWang.Lin@Sun.COM 
1715*9999SWang.Lin@Sun.COM 	return (0);
1716*9999SWang.Lin@Sun.COM }
1717*9999SWang.Lin@Sun.COM /*
1718*9999SWang.Lin@Sun.COM  * Allocate a single key cache slot.
1719*9999SWang.Lin@Sun.COM  */
1720*9999SWang.Lin@Sun.COM static int
1721*9999SWang.Lin@Sun.COM arn_key_alloc_single(struct arn_softc *sc, ieee80211_keyix *txkeyix,
1722*9999SWang.Lin@Sun.COM     ieee80211_keyix *rxkeyix)
1723*9999SWang.Lin@Sun.COM {
1724*9999SWang.Lin@Sun.COM 	uint16_t i, keyix;
1725*9999SWang.Lin@Sun.COM 
1726*9999SWang.Lin@Sun.COM 	/* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
1727*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_keymap); i++) {
1728*9999SWang.Lin@Sun.COM 		uint8_t b = sc->sc_keymap[i];
1729*9999SWang.Lin@Sun.COM 
1730*9999SWang.Lin@Sun.COM 		if (b != 0xff) {
1731*9999SWang.Lin@Sun.COM 			/*
1732*9999SWang.Lin@Sun.COM 			 * One or more slots are free.
1733*9999SWang.Lin@Sun.COM 			 */
1734*9999SWang.Lin@Sun.COM 			keyix = i*NBBY;
1735*9999SWang.Lin@Sun.COM 			while (b & 1)
1736*9999SWang.Lin@Sun.COM 				keyix++, b >>= 1;
1737*9999SWang.Lin@Sun.COM 			set_bit(keyix, sc->sc_keymap);
1738*9999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_single(): "
1739*9999SWang.Lin@Sun.COM 			    "key %u\n", keyix));
1740*9999SWang.Lin@Sun.COM 			*txkeyix = *rxkeyix = keyix;
1741*9999SWang.Lin@Sun.COM 			return (1);
1742*9999SWang.Lin@Sun.COM 		}
1743*9999SWang.Lin@Sun.COM 	}
1744*9999SWang.Lin@Sun.COM 	return (0);
1745*9999SWang.Lin@Sun.COM }
1746*9999SWang.Lin@Sun.COM 
1747*9999SWang.Lin@Sun.COM /*
1748*9999SWang.Lin@Sun.COM  * Allocate one or more key cache slots for a unicast key.  The
1749*9999SWang.Lin@Sun.COM  * key itself is needed only to identify the cipher.  For hardware
1750*9999SWang.Lin@Sun.COM  * TKIP with split cipher+MIC keys we allocate two key cache slot
1751*9999SWang.Lin@Sun.COM  * pairs so that we can setup separate TX and RX MIC keys.  Note
1752*9999SWang.Lin@Sun.COM  * that the MIC key for a TKIP key at slot i is assumed by the
1753*9999SWang.Lin@Sun.COM  * hardware to be at slot i+64.  This limits TKIP keys to the first
1754*9999SWang.Lin@Sun.COM  * 64 entries.
1755*9999SWang.Lin@Sun.COM  */
1756*9999SWang.Lin@Sun.COM /* ARGSUSED */
1757*9999SWang.Lin@Sun.COM int
1758*9999SWang.Lin@Sun.COM arn_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
1759*9999SWang.Lin@Sun.COM     ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
1760*9999SWang.Lin@Sun.COM {
1761*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1762*9999SWang.Lin@Sun.COM 
1763*9999SWang.Lin@Sun.COM 	/*
1764*9999SWang.Lin@Sun.COM 	 * We allocate two pair for TKIP when using the h/w to do
1765*9999SWang.Lin@Sun.COM 	 * the MIC.  For everything else, including software crypto,
1766*9999SWang.Lin@Sun.COM 	 * we allocate a single entry.  Note that s/w crypto requires
1767*9999SWang.Lin@Sun.COM 	 * a pass-through slot on the 5211 and 5212.  The 5210 does
1768*9999SWang.Lin@Sun.COM 	 * not support pass-through cache entries and we map all
1769*9999SWang.Lin@Sun.COM 	 * those requests to slot 0.
1770*9999SWang.Lin@Sun.COM 	 */
1771*9999SWang.Lin@Sun.COM 	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
1772*9999SWang.Lin@Sun.COM 		return (arn_key_alloc_single(sc, keyix, rxkeyix));
1773*9999SWang.Lin@Sun.COM 	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
1774*9999SWang.Lin@Sun.COM 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
1775*9999SWang.Lin@Sun.COM 		if (sc->sc_splitmic)
1776*9999SWang.Lin@Sun.COM 			return (arn_key_alloc_2pair(sc, keyix, rxkeyix));
1777*9999SWang.Lin@Sun.COM 		else
1778*9999SWang.Lin@Sun.COM 			return (arn_key_alloc_pair(sc, keyix, rxkeyix));
1779*9999SWang.Lin@Sun.COM 	} else {
1780*9999SWang.Lin@Sun.COM 		return (arn_key_alloc_single(sc, keyix, rxkeyix));
1781*9999SWang.Lin@Sun.COM 	}
1782*9999SWang.Lin@Sun.COM }
1783*9999SWang.Lin@Sun.COM 
1784*9999SWang.Lin@Sun.COM /*
1785*9999SWang.Lin@Sun.COM  * Delete an entry in the key cache allocated by ath_key_alloc.
1786*9999SWang.Lin@Sun.COM  */
1787*9999SWang.Lin@Sun.COM int
1788*9999SWang.Lin@Sun.COM arn_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
1789*9999SWang.Lin@Sun.COM {
1790*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1791*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
1792*9999SWang.Lin@Sun.COM 	const struct ieee80211_cipher *cip = k->wk_cipher;
1793*9999SWang.Lin@Sun.COM 	ieee80211_keyix keyix = k->wk_keyix;
1794*9999SWang.Lin@Sun.COM 
1795*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_delete():"
1796*9999SWang.Lin@Sun.COM 	    " delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher));
1797*9999SWang.Lin@Sun.COM 
1798*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_keyreset(ah, keyix);
1799*9999SWang.Lin@Sun.COM 	/*
1800*9999SWang.Lin@Sun.COM 	 * Handle split tx/rx keying required for TKIP with h/w MIC.
1801*9999SWang.Lin@Sun.COM 	 */
1802*9999SWang.Lin@Sun.COM 	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
1803*9999SWang.Lin@Sun.COM 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic)
1804*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_keyreset(ah, keyix+32);		/* RX key */
1805*9999SWang.Lin@Sun.COM 
1806*9999SWang.Lin@Sun.COM 	if (keyix >= IEEE80211_WEP_NKID) {
1807*9999SWang.Lin@Sun.COM 		/*
1808*9999SWang.Lin@Sun.COM 		 * Don't touch keymap entries for global keys so
1809*9999SWang.Lin@Sun.COM 		 * they are never considered for dynamic allocation.
1810*9999SWang.Lin@Sun.COM 		 */
1811*9999SWang.Lin@Sun.COM 		clr_bit(keyix, sc->sc_keymap);
1812*9999SWang.Lin@Sun.COM 		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
1813*9999SWang.Lin@Sun.COM 		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
1814*9999SWang.Lin@Sun.COM 			/*
1815*9999SWang.Lin@Sun.COM 			 * If splitmic is true +64 is TX key MIC,
1816*9999SWang.Lin@Sun.COM 			 * else +64 is RX key + RX key MIC.
1817*9999SWang.Lin@Sun.COM 			 */
1818*9999SWang.Lin@Sun.COM 			clr_bit(keyix+64, sc->sc_keymap);
1819*9999SWang.Lin@Sun.COM 			if (sc->sc_splitmic) {
1820*9999SWang.Lin@Sun.COM 				/* Rx key */
1821*9999SWang.Lin@Sun.COM 				clr_bit(keyix+32, sc->sc_keymap);
1822*9999SWang.Lin@Sun.COM 				/* RX key MIC */
1823*9999SWang.Lin@Sun.COM 				clr_bit(keyix+32+64, sc->sc_keymap);
1824*9999SWang.Lin@Sun.COM 			}
1825*9999SWang.Lin@Sun.COM 		}
1826*9999SWang.Lin@Sun.COM 	}
1827*9999SWang.Lin@Sun.COM 	return (1);
1828*9999SWang.Lin@Sun.COM }
1829*9999SWang.Lin@Sun.COM 
1830*9999SWang.Lin@Sun.COM /*
1831*9999SWang.Lin@Sun.COM  * Set a TKIP key into the hardware.  This handles the
1832*9999SWang.Lin@Sun.COM  * potential distribution of key state to multiple key
1833*9999SWang.Lin@Sun.COM  * cache slots for TKIP.
1834*9999SWang.Lin@Sun.COM  */
1835*9999SWang.Lin@Sun.COM static int
1836*9999SWang.Lin@Sun.COM arn_keyset_tkip(struct arn_softc *sc, const struct ieee80211_key *k,
1837*9999SWang.Lin@Sun.COM     struct ath9k_keyval *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
1838*9999SWang.Lin@Sun.COM {
1839*9999SWang.Lin@Sun.COM 	uint8_t *key_rxmic = NULL;
1840*9999SWang.Lin@Sun.COM 	uint8_t *key_txmic = NULL;
1841*9999SWang.Lin@Sun.COM 	uint8_t  *key = (uint8_t *)&(k->wk_key[0]);
1842*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
1843*9999SWang.Lin@Sun.COM 
1844*9999SWang.Lin@Sun.COM 	key_txmic = key + 16;
1845*9999SWang.Lin@Sun.COM 	key_rxmic = key + 24;
1846*9999SWang.Lin@Sun.COM 
1847*9999SWang.Lin@Sun.COM 	if (mac == NULL) {
1848*9999SWang.Lin@Sun.COM 		/* Group key installation */
1849*9999SWang.Lin@Sun.COM 		(void) memcpy(hk->kv_mic,  key_rxmic, sizeof (hk->kv_mic));
1850*9999SWang.Lin@Sun.COM 		return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk,
1851*9999SWang.Lin@Sun.COM 		    mac, B_FALSE));
1852*9999SWang.Lin@Sun.COM 	}
1853*9999SWang.Lin@Sun.COM 	if (!sc->sc_splitmic) {
1854*9999SWang.Lin@Sun.COM 		/*
1855*9999SWang.Lin@Sun.COM 		 * data key goes at first index,
1856*9999SWang.Lin@Sun.COM 		 * the hal handles the MIC keys at index+64.
1857*9999SWang.Lin@Sun.COM 		 */
1858*9999SWang.Lin@Sun.COM 		(void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic));
1859*9999SWang.Lin@Sun.COM 		(void) memcpy(hk->kv_txmic, key_txmic, sizeof (hk->kv_txmic));
1860*9999SWang.Lin@Sun.COM 		return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk,
1861*9999SWang.Lin@Sun.COM 		    mac, B_FALSE));
1862*9999SWang.Lin@Sun.COM 	}
1863*9999SWang.Lin@Sun.COM 	/*
1864*9999SWang.Lin@Sun.COM 	 * TX key goes at first index, RX key at +32.
1865*9999SWang.Lin@Sun.COM 	 * The hal handles the MIC keys at index+64.
1866*9999SWang.Lin@Sun.COM 	 */
1867*9999SWang.Lin@Sun.COM 	(void) memcpy(hk->kv_mic, key_txmic, sizeof (hk->kv_mic));
1868*9999SWang.Lin@Sun.COM 	if (!(ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, NULL,
1869*9999SWang.Lin@Sun.COM 	    B_FALSE))) {
1870*9999SWang.Lin@Sun.COM 		/* Txmic entry failed. No need to proceed further */
1871*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_KEYCACHE,
1872*9999SWang.Lin@Sun.COM 		    "%s Setting TX MIC Key Failed\n", __func__));
1873*9999SWang.Lin@Sun.COM 		return (0);
1874*9999SWang.Lin@Sun.COM 	}
1875*9999SWang.Lin@Sun.COM 
1876*9999SWang.Lin@Sun.COM 	(void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic));
1877*9999SWang.Lin@Sun.COM 
1878*9999SWang.Lin@Sun.COM 	/* XXX delete tx key on failure? */
1879*9999SWang.Lin@Sun.COM 	return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, mac, B_FALSE));
1880*9999SWang.Lin@Sun.COM 
1881*9999SWang.Lin@Sun.COM }
1882*9999SWang.Lin@Sun.COM 
1883*9999SWang.Lin@Sun.COM int
1884*9999SWang.Lin@Sun.COM arn_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
1885*9999SWang.Lin@Sun.COM     const uint8_t mac[IEEE80211_ADDR_LEN])
1886*9999SWang.Lin@Sun.COM {
1887*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
1888*9999SWang.Lin@Sun.COM 	const struct ieee80211_cipher *cip = k->wk_cipher;
1889*9999SWang.Lin@Sun.COM 	struct ath9k_keyval hk;
1890*9999SWang.Lin@Sun.COM 
1891*9999SWang.Lin@Sun.COM 	/* cipher table */
1892*9999SWang.Lin@Sun.COM 	static const uint8_t ciphermap[] = {
1893*9999SWang.Lin@Sun.COM 		ATH9K_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
1894*9999SWang.Lin@Sun.COM 		ATH9K_CIPHER_TKIP,		/* IEEE80211_CIPHER_TKIP */
1895*9999SWang.Lin@Sun.COM 		ATH9K_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
1896*9999SWang.Lin@Sun.COM 		ATH9K_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
1897*9999SWang.Lin@Sun.COM 		ATH9K_CIPHER_CKIP,		/* IEEE80211_CIPHER_CKIP */
1898*9999SWang.Lin@Sun.COM 		ATH9K_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
1899*9999SWang.Lin@Sun.COM 	};
1900*9999SWang.Lin@Sun.COM 
1901*9999SWang.Lin@Sun.COM 	bzero(&hk, sizeof (hk));
1902*9999SWang.Lin@Sun.COM 
1903*9999SWang.Lin@Sun.COM 	/*
1904*9999SWang.Lin@Sun.COM 	 * Software crypto uses a "clear key" so non-crypto
1905*9999SWang.Lin@Sun.COM 	 * state kept in the key cache are maintainedd so that
1906*9999SWang.Lin@Sun.COM 	 * rx frames have an entry to match.
1907*9999SWang.Lin@Sun.COM 	 */
1908*9999SWang.Lin@Sun.COM 	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
1909*9999SWang.Lin@Sun.COM 		ASSERT(cip->ic_cipher < 6);
1910*9999SWang.Lin@Sun.COM 		hk.kv_type = ciphermap[cip->ic_cipher];
1911*9999SWang.Lin@Sun.COM 		hk.kv_len = k->wk_keylen;
1912*9999SWang.Lin@Sun.COM 		bcopy(k->wk_key, hk.kv_val, k->wk_keylen);
1913*9999SWang.Lin@Sun.COM 	} else {
1914*9999SWang.Lin@Sun.COM 		hk.kv_type = ATH9K_CIPHER_CLR;
1915*9999SWang.Lin@Sun.COM 	}
1916*9999SWang.Lin@Sun.COM 
1917*9999SWang.Lin@Sun.COM 	if (hk.kv_type == ATH9K_CIPHER_TKIP &&
1918*9999SWang.Lin@Sun.COM 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
1919*9999SWang.Lin@Sun.COM 		return (arn_keyset_tkip(sc, k, &hk, mac));
1920*9999SWang.Lin@Sun.COM 	} else {
1921*9999SWang.Lin@Sun.COM 		return (ath9k_hw_set_keycache_entry(sc->sc_ah,
1922*9999SWang.Lin@Sun.COM 		    k->wk_keyix, &hk, mac, B_FALSE));
1923*9999SWang.Lin@Sun.COM 	}
1924*9999SWang.Lin@Sun.COM }
1925*9999SWang.Lin@Sun.COM 
1926*9999SWang.Lin@Sun.COM /*
1927*9999SWang.Lin@Sun.COM  * Enable/Disable short slot timing
1928*9999SWang.Lin@Sun.COM  */
1929*9999SWang.Lin@Sun.COM void
1930*9999SWang.Lin@Sun.COM arn_set_shortslot(ieee80211com_t *ic, int onoff)
1931*9999SWang.Lin@Sun.COM {
1932*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = ((struct arn_softc *)ic)->sc_ah;
1933*9999SWang.Lin@Sun.COM 
1934*9999SWang.Lin@Sun.COM 	if (onoff)
1935*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9);
1936*9999SWang.Lin@Sun.COM 	else
1937*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_20);
1938*9999SWang.Lin@Sun.COM }
1939*9999SWang.Lin@Sun.COM 
1940*9999SWang.Lin@Sun.COM static int
1941*9999SWang.Lin@Sun.COM arn_open(struct arn_softc *sc)
1942*9999SWang.Lin@Sun.COM {
1943*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
1944*9999SWang.Lin@Sun.COM 	struct ieee80211_channel *curchan = ic->ic_curchan;
1945*9999SWang.Lin@Sun.COM 	struct ath9k_channel *init_channel;
1946*9999SWang.Lin@Sun.COM 	int error = 0, pos, status;
1947*9999SWang.Lin@Sun.COM 
1948*9999SWang.Lin@Sun.COM 	ARN_LOCK_ASSERT(sc);
1949*9999SWang.Lin@Sun.COM 
1950*9999SWang.Lin@Sun.COM 	pos = arn_get_channel(sc, curchan);
1951*9999SWang.Lin@Sun.COM 	if (pos == -1) {
1952*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: "
1953*9999SWang.Lin@Sun.COM 		    "%s: Invalid channel\n", __func__));
1954*9999SWang.Lin@Sun.COM 		error = EINVAL;
1955*9999SWang.Lin@Sun.COM 		goto error;
1956*9999SWang.Lin@Sun.COM 	}
1957*9999SWang.Lin@Sun.COM 
1958*9999SWang.Lin@Sun.COM 	sc->tx_chan_width = ATH9K_HT_MACMODE_20;
1959*9999SWang.Lin@Sun.COM 
1960*9999SWang.Lin@Sun.COM 	if (sc->sc_curmode == ATH9K_MODE_11A) {
1961*9999SWang.Lin@Sun.COM 		sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_A;
1962*9999SWang.Lin@Sun.COM 	} else {
1963*9999SWang.Lin@Sun.COM 		sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_G;
1964*9999SWang.Lin@Sun.COM 	}
1965*9999SWang.Lin@Sun.COM 
1966*9999SWang.Lin@Sun.COM 	init_channel = &sc->sc_ah->ah_channels[pos];
1967*9999SWang.Lin@Sun.COM 
1968*9999SWang.Lin@Sun.COM 	/* Reset SERDES registers */
1969*9999SWang.Lin@Sun.COM 	ath9k_hw_configpcipowersave(sc->sc_ah, 0);
1970*9999SWang.Lin@Sun.COM 
1971*9999SWang.Lin@Sun.COM 	/*
1972*9999SWang.Lin@Sun.COM 	 * The basic interface to setting the hardware in a good
1973*9999SWang.Lin@Sun.COM 	 * state is ``reset''.	On return the hardware is known to
1974*9999SWang.Lin@Sun.COM 	 * be powered up and with interrupts disabled.	This must
1975*9999SWang.Lin@Sun.COM 	 * be followed by initialization of the appropriate bits
1976*9999SWang.Lin@Sun.COM 	 * and then setup of the interrupt mask.
1977*9999SWang.Lin@Sun.COM 	 */
1978*9999SWang.Lin@Sun.COM 	if (!ath9k_hw_reset(sc->sc_ah, init_channel,
1979*9999SWang.Lin@Sun.COM 	    sc->tx_chan_width, sc->sc_tx_chainmask,
1980*9999SWang.Lin@Sun.COM 	    sc->sc_rx_chainmask, sc->sc_ht_extprotspacing,
1981*9999SWang.Lin@Sun.COM 	    B_FALSE, &status)) {
1982*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: "
1983*9999SWang.Lin@Sun.COM 		    "%s: unable to reset hardware; hal status %u "
1984*9999SWang.Lin@Sun.COM 		    "(freq %u flags 0x%x)\n", __func__, status,
1985*9999SWang.Lin@Sun.COM 		    init_channel->channel, init_channel->channelFlags));
1986*9999SWang.Lin@Sun.COM 
1987*9999SWang.Lin@Sun.COM 		error = EIO;
1988*9999SWang.Lin@Sun.COM 		goto error;
1989*9999SWang.Lin@Sun.COM 	}
1990*9999SWang.Lin@Sun.COM 
1991*9999SWang.Lin@Sun.COM 	/*
1992*9999SWang.Lin@Sun.COM 	 * This is needed only to setup initial state
1993*9999SWang.Lin@Sun.COM 	 * but it's best done after a reset.
1994*9999SWang.Lin@Sun.COM 	 */
1995*9999SWang.Lin@Sun.COM 	arn_update_txpow(sc);
1996*9999SWang.Lin@Sun.COM 
1997*9999SWang.Lin@Sun.COM 	/*
1998*9999SWang.Lin@Sun.COM 	 * Setup the hardware after reset:
1999*9999SWang.Lin@Sun.COM 	 * The receive engine is set going.
2000*9999SWang.Lin@Sun.COM 	 * Frame transmit is handled entirely
2001*9999SWang.Lin@Sun.COM 	 * in the frame output path; there's nothing to do
2002*9999SWang.Lin@Sun.COM 	 * here except setup the interrupt mask.
2003*9999SWang.Lin@Sun.COM 	 */
2004*9999SWang.Lin@Sun.COM 	if (arn_startrecv(sc) != 0) {
2005*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INIT, "arn: "
2006*9999SWang.Lin@Sun.COM 		    "%s: unable to start recv logic\n", __func__));
2007*9999SWang.Lin@Sun.COM 		error = EIO;
2008*9999SWang.Lin@Sun.COM 		goto error;
2009*9999SWang.Lin@Sun.COM 	}
2010*9999SWang.Lin@Sun.COM 
2011*9999SWang.Lin@Sun.COM 	/* Setup our intr mask. */
2012*9999SWang.Lin@Sun.COM 	sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX |
2013*9999SWang.Lin@Sun.COM 	    ATH9K_INT_RXEOL | ATH9K_INT_RXORN |
2014*9999SWang.Lin@Sun.COM 	    ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
2015*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT
2016*9999SWang.Lin@Sun.COM 	if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_GTT)
2017*9999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_GTT;
2018*9999SWang.Lin@Sun.COM #endif
2019*9999SWang.Lin@Sun.COM 
2020*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT
2021*9999SWang.Lin@Sun.COM 	if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
2022*9999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_CST;
2023*9999SWang.Lin@Sun.COM #endif
2024*9999SWang.Lin@Sun.COM 
2025*9999SWang.Lin@Sun.COM 	/*
2026*9999SWang.Lin@Sun.COM 	 * Enable MIB interrupts when there are hardware phy counters.
2027*9999SWang.Lin@Sun.COM 	 * Note we only do this (at the moment) for station mode.
2028*9999SWang.Lin@Sun.COM 	 */
2029*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB
2030*9999SWang.Lin@Sun.COM 	if (ath9k_hw_phycounters(sc->sc_ah) &&
2031*9999SWang.Lin@Sun.COM 	    ((sc->sc_ah->ah_opmode == ATH9K_M_STA) ||
2032*9999SWang.Lin@Sun.COM 	    (sc->sc_ah->ah_opmode == ATH9K_M_IBSS)))
2033*9999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_MIB;
2034*9999SWang.Lin@Sun.COM #endif
2035*9999SWang.Lin@Sun.COM 	/*
2036*9999SWang.Lin@Sun.COM 	 * Some hardware processes the TIM IE and fires an
2037*9999SWang.Lin@Sun.COM 	 * interrupt when the TIM bit is set.  For hardware
2038*9999SWang.Lin@Sun.COM 	 * that does, if not overridden by configuration,
2039*9999SWang.Lin@Sun.COM 	 * enable the TIM interrupt when operating as station.
2040*9999SWang.Lin@Sun.COM 	 */
2041*9999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM
2042*9999SWang.Lin@Sun.COM 	if ((sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) &&
2043*9999SWang.Lin@Sun.COM 	    (sc->sc_ah->ah_opmode == ATH9K_M_STA) &&
2044*9999SWang.Lin@Sun.COM 	    !sc->sc_config.swBeaconProcess)
2045*9999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_TIM;
2046*9999SWang.Lin@Sun.COM #endif
2047*9999SWang.Lin@Sun.COM 	if (arn_chan2mode(init_channel) != sc->sc_curmode)
2048*9999SWang.Lin@Sun.COM 		arn_setcurmode(sc, arn_chan2mode(init_channel));
2049*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: "
2050*9999SWang.Lin@Sun.COM 	    "%s: current mode after arn_setcurmode is %d\n",
2051*9999SWang.Lin@Sun.COM 	    __func__, sc->sc_curmode));
2052*9999SWang.Lin@Sun.COM 
2053*9999SWang.Lin@Sun.COM 	sc->sc_isrunning = 1;
2054*9999SWang.Lin@Sun.COM 
2055*9999SWang.Lin@Sun.COM 	/* Disable BMISS interrupt when we're not associated */
2056*9999SWang.Lin@Sun.COM 	sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
2057*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
2058*9999SWang.Lin@Sun.COM 
2059*9999SWang.Lin@Sun.COM 	return (0);
2060*9999SWang.Lin@Sun.COM 
2061*9999SWang.Lin@Sun.COM error:
2062*9999SWang.Lin@Sun.COM 	return (error);
2063*9999SWang.Lin@Sun.COM }
2064*9999SWang.Lin@Sun.COM 
2065*9999SWang.Lin@Sun.COM static void
2066*9999SWang.Lin@Sun.COM arn_close(struct arn_softc *sc)
2067*9999SWang.Lin@Sun.COM {
2068*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
2069*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
2070*9999SWang.Lin@Sun.COM 
2071*9999SWang.Lin@Sun.COM 	ARN_LOCK_ASSERT(sc);
2072*9999SWang.Lin@Sun.COM 
2073*9999SWang.Lin@Sun.COM 	if (!sc->sc_isrunning)
2074*9999SWang.Lin@Sun.COM 		return;
2075*9999SWang.Lin@Sun.COM 
2076*9999SWang.Lin@Sun.COM 	/*
2077*9999SWang.Lin@Sun.COM 	 * Shutdown the hardware and driver
2078*9999SWang.Lin@Sun.COM 	 * Note that some of this work is not possible if the
2079*9999SWang.Lin@Sun.COM 	 * hardware is gone (invalid).
2080*9999SWang.Lin@Sun.COM 	 */
2081*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2082*9999SWang.Lin@Sun.COM 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2083*9999SWang.Lin@Sun.COM 	ieee80211_stop_watchdog(ic);
2084*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2085*9999SWang.Lin@Sun.COM 
2086*9999SWang.Lin@Sun.COM 	/*
2087*9999SWang.Lin@Sun.COM 	 * make sure h/w will not generate any interrupt
2088*9999SWang.Lin@Sun.COM 	 * before setting the invalid flag.
2089*9999SWang.Lin@Sun.COM 	 */
2090*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, 0);
2091*9999SWang.Lin@Sun.COM 
2092*9999SWang.Lin@Sun.COM 	if (!(sc->sc_flags & SC_OP_INVALID)) {
2093*9999SWang.Lin@Sun.COM 		arn_draintxq(sc, 0);
2094*9999SWang.Lin@Sun.COM 		(void) arn_stoprecv(sc);
2095*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_phy_disable(ah);
2096*9999SWang.Lin@Sun.COM 	} else {
2097*9999SWang.Lin@Sun.COM 		sc->sc_rxlink = NULL;
2098*9999SWang.Lin@Sun.COM 	}
2099*9999SWang.Lin@Sun.COM 
2100*9999SWang.Lin@Sun.COM 	sc->sc_isrunning = 0;
2101*9999SWang.Lin@Sun.COM }
2102*9999SWang.Lin@Sun.COM 
2103*9999SWang.Lin@Sun.COM /*
2104*9999SWang.Lin@Sun.COM  * MAC callback functions
2105*9999SWang.Lin@Sun.COM  */
2106*9999SWang.Lin@Sun.COM static int
2107*9999SWang.Lin@Sun.COM arn_m_stat(void *arg, uint_t stat, uint64_t *val)
2108*9999SWang.Lin@Sun.COM {
2109*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2110*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
2111*9999SWang.Lin@Sun.COM 	struct ieee80211_node *in;
2112*9999SWang.Lin@Sun.COM 	struct ieee80211_rateset *rs;
2113*9999SWang.Lin@Sun.COM 
2114*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2115*9999SWang.Lin@Sun.COM 	switch (stat) {
2116*9999SWang.Lin@Sun.COM 	case MAC_STAT_IFSPEED:
2117*9999SWang.Lin@Sun.COM 		in = ic->ic_bss;
2118*9999SWang.Lin@Sun.COM 		rs = &in->in_rates;
2119*9999SWang.Lin@Sun.COM 		*val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 *
2120*9999SWang.Lin@Sun.COM 		    1000000ull;
2121*9999SWang.Lin@Sun.COM 		break;
2122*9999SWang.Lin@Sun.COM 	case MAC_STAT_NOXMTBUF:
2123*9999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_tx_nobuf +
2124*9999SWang.Lin@Sun.COM 		    sc->sc_stats.ast_tx_nobufmgt;
2125*9999SWang.Lin@Sun.COM 		break;
2126*9999SWang.Lin@Sun.COM 	case MAC_STAT_IERRORS:
2127*9999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_rx_tooshort;
2128*9999SWang.Lin@Sun.COM 		break;
2129*9999SWang.Lin@Sun.COM 	case MAC_STAT_RBYTES:
2130*9999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_rx_bytes;
2131*9999SWang.Lin@Sun.COM 		break;
2132*9999SWang.Lin@Sun.COM 	case MAC_STAT_IPACKETS:
2133*9999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_rx_frags;
2134*9999SWang.Lin@Sun.COM 		break;
2135*9999SWang.Lin@Sun.COM 	case MAC_STAT_OBYTES:
2136*9999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_tx_bytes;
2137*9999SWang.Lin@Sun.COM 		break;
2138*9999SWang.Lin@Sun.COM 	case MAC_STAT_OPACKETS:
2139*9999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_tx_frags;
2140*9999SWang.Lin@Sun.COM 		break;
2141*9999SWang.Lin@Sun.COM 	case MAC_STAT_OERRORS:
2142*9999SWang.Lin@Sun.COM 	case WIFI_STAT_TX_FAILED:
2143*9999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_tx_fifoerr +
2144*9999SWang.Lin@Sun.COM 		    sc->sc_stats.ast_tx_xretries +
2145*9999SWang.Lin@Sun.COM 		    sc->sc_stats.ast_tx_discard;
2146*9999SWang.Lin@Sun.COM 		break;
2147*9999SWang.Lin@Sun.COM 	case WIFI_STAT_TX_RETRANS:
2148*9999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_tx_xretries;
2149*9999SWang.Lin@Sun.COM 		break;
2150*9999SWang.Lin@Sun.COM 	case WIFI_STAT_FCS_ERRORS:
2151*9999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_rx_crcerr;
2152*9999SWang.Lin@Sun.COM 		break;
2153*9999SWang.Lin@Sun.COM 	case WIFI_STAT_WEP_ERRORS:
2154*9999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_rx_badcrypt;
2155*9999SWang.Lin@Sun.COM 		break;
2156*9999SWang.Lin@Sun.COM 	case WIFI_STAT_TX_FRAGS:
2157*9999SWang.Lin@Sun.COM 	case WIFI_STAT_MCAST_TX:
2158*9999SWang.Lin@Sun.COM 	case WIFI_STAT_RTS_SUCCESS:
2159*9999SWang.Lin@Sun.COM 	case WIFI_STAT_RTS_FAILURE:
2160*9999SWang.Lin@Sun.COM 	case WIFI_STAT_ACK_FAILURE:
2161*9999SWang.Lin@Sun.COM 	case WIFI_STAT_RX_FRAGS:
2162*9999SWang.Lin@Sun.COM 	case WIFI_STAT_MCAST_RX:
2163*9999SWang.Lin@Sun.COM 	case WIFI_STAT_RX_DUPS:
2164*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
2165*9999SWang.Lin@Sun.COM 		return (ieee80211_stat(ic, stat, val));
2166*9999SWang.Lin@Sun.COM 	default:
2167*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
2168*9999SWang.Lin@Sun.COM 		return (ENOTSUP);
2169*9999SWang.Lin@Sun.COM 	}
2170*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2171*9999SWang.Lin@Sun.COM 
2172*9999SWang.Lin@Sun.COM 	return (0);
2173*9999SWang.Lin@Sun.COM }
2174*9999SWang.Lin@Sun.COM 
2175*9999SWang.Lin@Sun.COM int
2176*9999SWang.Lin@Sun.COM arn_m_start(void *arg)
2177*9999SWang.Lin@Sun.COM {
2178*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2179*9999SWang.Lin@Sun.COM 	int err = 0;
2180*9999SWang.Lin@Sun.COM 
2181*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2182*9999SWang.Lin@Sun.COM 
2183*9999SWang.Lin@Sun.COM 	/*
2184*9999SWang.Lin@Sun.COM 	 * Stop anything previously setup.  This is safe
2185*9999SWang.Lin@Sun.COM 	 * whether this is the first time through or not.
2186*9999SWang.Lin@Sun.COM 	 */
2187*9999SWang.Lin@Sun.COM 
2188*9999SWang.Lin@Sun.COM 	arn_close(sc);
2189*9999SWang.Lin@Sun.COM 
2190*9999SWang.Lin@Sun.COM 	if ((err = arn_open(sc)) != 0) {
2191*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
2192*9999SWang.Lin@Sun.COM 		return (err);
2193*9999SWang.Lin@Sun.COM 	}
2194*9999SWang.Lin@Sun.COM 
2195*9999SWang.Lin@Sun.COM 	/* H/W is reday now */
2196*9999SWang.Lin@Sun.COM 	sc->sc_flags &= ~SC_OP_INVALID;
2197*9999SWang.Lin@Sun.COM 
2198*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2199*9999SWang.Lin@Sun.COM 
2200*9999SWang.Lin@Sun.COM 	return (0);
2201*9999SWang.Lin@Sun.COM }
2202*9999SWang.Lin@Sun.COM 
2203*9999SWang.Lin@Sun.COM static void
2204*9999SWang.Lin@Sun.COM arn_m_stop(void *arg)
2205*9999SWang.Lin@Sun.COM {
2206*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2207*9999SWang.Lin@Sun.COM 
2208*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2209*9999SWang.Lin@Sun.COM 	arn_close(sc);
2210*9999SWang.Lin@Sun.COM 
2211*9999SWang.Lin@Sun.COM 	/* disable HAL and put h/w to sleep */
2212*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_disable(sc->sc_ah);
2213*9999SWang.Lin@Sun.COM 	ath9k_hw_configpcipowersave(sc->sc_ah, 1);
2214*9999SWang.Lin@Sun.COM 
2215*9999SWang.Lin@Sun.COM 	/* XXX: hardware will not be ready in suspend state */
2216*9999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
2217*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2218*9999SWang.Lin@Sun.COM }
2219*9999SWang.Lin@Sun.COM 
2220*9999SWang.Lin@Sun.COM static int
2221*9999SWang.Lin@Sun.COM arn_m_promisc(void *arg, boolean_t on)
2222*9999SWang.Lin@Sun.COM {
2223*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2224*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
2225*9999SWang.Lin@Sun.COM 	uint32_t rfilt;
2226*9999SWang.Lin@Sun.COM 
2227*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2228*9999SWang.Lin@Sun.COM 
2229*9999SWang.Lin@Sun.COM 	rfilt = ath9k_hw_getrxfilter(ah);
2230*9999SWang.Lin@Sun.COM 	if (on)
2231*9999SWang.Lin@Sun.COM 		rfilt |= ATH9K_RX_FILTER_PROM;
2232*9999SWang.Lin@Sun.COM 	else
2233*9999SWang.Lin@Sun.COM 		rfilt &= ~ATH9K_RX_FILTER_PROM;
2234*9999SWang.Lin@Sun.COM 	sc->sc_promisc = on;
2235*9999SWang.Lin@Sun.COM 	ath9k_hw_setrxfilter(ah, rfilt);
2236*9999SWang.Lin@Sun.COM 
2237*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2238*9999SWang.Lin@Sun.COM 
2239*9999SWang.Lin@Sun.COM 	return (0);
2240*9999SWang.Lin@Sun.COM }
2241*9999SWang.Lin@Sun.COM 
2242*9999SWang.Lin@Sun.COM static int
2243*9999SWang.Lin@Sun.COM arn_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
2244*9999SWang.Lin@Sun.COM {
2245*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2246*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
2247*9999SWang.Lin@Sun.COM 	uint32_t val, index, bit;
2248*9999SWang.Lin@Sun.COM 	uint8_t pos;
2249*9999SWang.Lin@Sun.COM 	uint32_t *mfilt = sc->sc_mcast_hash;
2250*9999SWang.Lin@Sun.COM 
2251*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2252*9999SWang.Lin@Sun.COM 
2253*9999SWang.Lin@Sun.COM 	/* calculate XOR of eight 6bit values */
2254*9999SWang.Lin@Sun.COM 	val = ARN_LE_READ_32(mca + 0);
2255*9999SWang.Lin@Sun.COM 	pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
2256*9999SWang.Lin@Sun.COM 	val = ARN_LE_READ_32(mca + 3);
2257*9999SWang.Lin@Sun.COM 	pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
2258*9999SWang.Lin@Sun.COM 	pos &= 0x3f;
2259*9999SWang.Lin@Sun.COM 	index = pos / 32;
2260*9999SWang.Lin@Sun.COM 	bit = 1 << (pos % 32);
2261*9999SWang.Lin@Sun.COM 
2262*9999SWang.Lin@Sun.COM 	if (add) {	/* enable multicast */
2263*9999SWang.Lin@Sun.COM 		sc->sc_mcast_refs[pos]++;
2264*9999SWang.Lin@Sun.COM 		mfilt[index] |= bit;
2265*9999SWang.Lin@Sun.COM 	} else {	/* disable multicast */
2266*9999SWang.Lin@Sun.COM 		if (--sc->sc_mcast_refs[pos] == 0)
2267*9999SWang.Lin@Sun.COM 			mfilt[index] &= ~bit;
2268*9999SWang.Lin@Sun.COM 	}
2269*9999SWang.Lin@Sun.COM 	ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
2270*9999SWang.Lin@Sun.COM 
2271*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2272*9999SWang.Lin@Sun.COM 	return (0);
2273*9999SWang.Lin@Sun.COM }
2274*9999SWang.Lin@Sun.COM 
2275*9999SWang.Lin@Sun.COM static int
2276*9999SWang.Lin@Sun.COM arn_m_unicst(void *arg, const uint8_t *macaddr)
2277*9999SWang.Lin@Sun.COM {
2278*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2279*9999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
2280*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
2281*9999SWang.Lin@Sun.COM 
2282*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_XMIT, "ath: ath_gld_saddr(): "
2283*9999SWang.Lin@Sun.COM 	    "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
2284*9999SWang.Lin@Sun.COM 	    macaddr[0], macaddr[1], macaddr[2],
2285*9999SWang.Lin@Sun.COM 	    macaddr[3], macaddr[4], macaddr[5]));
2286*9999SWang.Lin@Sun.COM 
2287*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2288*9999SWang.Lin@Sun.COM 	IEEE80211_ADDR_COPY(sc->sc_isc.ic_macaddr, macaddr);
2289*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_setmac(ah, sc->sc_isc.ic_macaddr);
2290*9999SWang.Lin@Sun.COM 	(void) arn_reset(ic);
2291*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2292*9999SWang.Lin@Sun.COM 	return (0);
2293*9999SWang.Lin@Sun.COM }
2294*9999SWang.Lin@Sun.COM 
2295*9999SWang.Lin@Sun.COM static mblk_t *
2296*9999SWang.Lin@Sun.COM arn_m_tx(void *arg, mblk_t *mp)
2297*9999SWang.Lin@Sun.COM {
2298*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2299*9999SWang.Lin@Sun.COM 	int error = 0;
2300*9999SWang.Lin@Sun.COM 	mblk_t *next;
2301*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
2302*9999SWang.Lin@Sun.COM 
2303*9999SWang.Lin@Sun.COM 	/*
2304*9999SWang.Lin@Sun.COM 	 * No data frames go out unless we're associated; this
2305*9999SWang.Lin@Sun.COM 	 * should not happen as the 802.11 layer does not enable
2306*9999SWang.Lin@Sun.COM 	 * the xmit queue until we enter the RUN state.
2307*9999SWang.Lin@Sun.COM 	 */
2308*9999SWang.Lin@Sun.COM 	if (ic->ic_state != IEEE80211_S_RUN) {
2309*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_XMIT, "arn: arn_m_tx(): "
2310*9999SWang.Lin@Sun.COM 		    "discard, state %u\n", ic->ic_state));
2311*9999SWang.Lin@Sun.COM 		sc->sc_stats.ast_tx_discard++;
2312*9999SWang.Lin@Sun.COM 		freemsgchain(mp);
2313*9999SWang.Lin@Sun.COM 		return (NULL);
2314*9999SWang.Lin@Sun.COM 	}
2315*9999SWang.Lin@Sun.COM 
2316*9999SWang.Lin@Sun.COM 	while (mp != NULL) {
2317*9999SWang.Lin@Sun.COM 		next = mp->b_next;
2318*9999SWang.Lin@Sun.COM 		mp->b_next = NULL;
2319*9999SWang.Lin@Sun.COM 		error = arn_tx(ic, mp, IEEE80211_FC0_TYPE_DATA);
2320*9999SWang.Lin@Sun.COM 		if (error != 0) {
2321*9999SWang.Lin@Sun.COM 			mp->b_next = next;
2322*9999SWang.Lin@Sun.COM 			if (error == ENOMEM) {
2323*9999SWang.Lin@Sun.COM 				break;
2324*9999SWang.Lin@Sun.COM 			} else {
2325*9999SWang.Lin@Sun.COM 				freemsgchain(mp);
2326*9999SWang.Lin@Sun.COM 				return (NULL);
2327*9999SWang.Lin@Sun.COM 			}
2328*9999SWang.Lin@Sun.COM 		}
2329*9999SWang.Lin@Sun.COM 		mp = next;
2330*9999SWang.Lin@Sun.COM 	}
2331*9999SWang.Lin@Sun.COM 
2332*9999SWang.Lin@Sun.COM 	return (mp);
2333*9999SWang.Lin@Sun.COM }
2334*9999SWang.Lin@Sun.COM 
2335*9999SWang.Lin@Sun.COM static void
2336*9999SWang.Lin@Sun.COM arn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
2337*9999SWang.Lin@Sun.COM {
2338*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2339*9999SWang.Lin@Sun.COM 	int32_t err;
2340*9999SWang.Lin@Sun.COM 
2341*9999SWang.Lin@Sun.COM 	err = ieee80211_ioctl(&sc->sc_isc, wq, mp);
2342*9999SWang.Lin@Sun.COM 
2343*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2344*9999SWang.Lin@Sun.COM 	if (err == ENETRESET) {
2345*9999SWang.Lin@Sun.COM 		if (!(sc->sc_flags & SC_OP_INVALID)) {
2346*9999SWang.Lin@Sun.COM 			ARN_UNLOCK(sc);
2347*9999SWang.Lin@Sun.COM 
2348*9999SWang.Lin@Sun.COM 			(void) arn_m_start(sc);
2349*9999SWang.Lin@Sun.COM 
2350*9999SWang.Lin@Sun.COM 			(void) ieee80211_new_state(&sc->sc_isc,
2351*9999SWang.Lin@Sun.COM 			    IEEE80211_S_SCAN, -1);
2352*9999SWang.Lin@Sun.COM 			ARN_LOCK(sc);
2353*9999SWang.Lin@Sun.COM 		}
2354*9999SWang.Lin@Sun.COM 	}
2355*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2356*9999SWang.Lin@Sun.COM }
2357*9999SWang.Lin@Sun.COM 
2358*9999SWang.Lin@Sun.COM static int
2359*9999SWang.Lin@Sun.COM arn_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2360*9999SWang.Lin@Sun.COM     uint_t wldp_length, const void *wldp_buf)
2361*9999SWang.Lin@Sun.COM {
2362*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2363*9999SWang.Lin@Sun.COM 	int	err;
2364*9999SWang.Lin@Sun.COM 
2365*9999SWang.Lin@Sun.COM 	err = ieee80211_setprop(&sc->sc_isc, pr_name, wldp_pr_num,
2366*9999SWang.Lin@Sun.COM 	    wldp_length, wldp_buf);
2367*9999SWang.Lin@Sun.COM 
2368*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2369*9999SWang.Lin@Sun.COM 
2370*9999SWang.Lin@Sun.COM 	if (err == ENETRESET) {
2371*9999SWang.Lin@Sun.COM 		if (!(sc->sc_flags & SC_OP_INVALID)) {
2372*9999SWang.Lin@Sun.COM 			ARN_UNLOCK(sc);
2373*9999SWang.Lin@Sun.COM 			(void) arn_m_start(sc);
2374*9999SWang.Lin@Sun.COM 			(void) ieee80211_new_state(&sc->sc_isc,
2375*9999SWang.Lin@Sun.COM 			    IEEE80211_S_SCAN, -1);
2376*9999SWang.Lin@Sun.COM 			ARN_LOCK(sc);
2377*9999SWang.Lin@Sun.COM 		}
2378*9999SWang.Lin@Sun.COM 		err = 0;
2379*9999SWang.Lin@Sun.COM 	}
2380*9999SWang.Lin@Sun.COM 
2381*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2382*9999SWang.Lin@Sun.COM 
2383*9999SWang.Lin@Sun.COM 	return (err);
2384*9999SWang.Lin@Sun.COM }
2385*9999SWang.Lin@Sun.COM 
2386*9999SWang.Lin@Sun.COM /* ARGSUSED */
2387*9999SWang.Lin@Sun.COM static int
2388*9999SWang.Lin@Sun.COM arn_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2389*9999SWang.Lin@Sun.COM     uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm)
2390*9999SWang.Lin@Sun.COM {
2391*9999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
2392*9999SWang.Lin@Sun.COM 	int	err = 0;
2393*9999SWang.Lin@Sun.COM 
2394*9999SWang.Lin@Sun.COM 	err = ieee80211_getprop(&sc->sc_isc, pr_name, wldp_pr_num,
2395*9999SWang.Lin@Sun.COM 	    pr_flags, wldp_length, wldp_buf, perm);
2396*9999SWang.Lin@Sun.COM 
2397*9999SWang.Lin@Sun.COM 	return (err);
2398*9999SWang.Lin@Sun.COM }
2399*9999SWang.Lin@Sun.COM 
2400*9999SWang.Lin@Sun.COM /* return bus cachesize in 4B word units */
2401*9999SWang.Lin@Sun.COM static void
2402*9999SWang.Lin@Sun.COM arn_pci_config_cachesize(struct arn_softc *sc)
2403*9999SWang.Lin@Sun.COM {
2404*9999SWang.Lin@Sun.COM 	uint8_t csz;
2405*9999SWang.Lin@Sun.COM 
2406*9999SWang.Lin@Sun.COM 	/*
2407*9999SWang.Lin@Sun.COM 	 * Cache line size is used to size and align various
2408*9999SWang.Lin@Sun.COM 	 * structures used to communicate with the hardware.
2409*9999SWang.Lin@Sun.COM 	 */
2410*9999SWang.Lin@Sun.COM 	csz = pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ);
2411*9999SWang.Lin@Sun.COM 	if (csz == 0) {
2412*9999SWang.Lin@Sun.COM 		/*
2413*9999SWang.Lin@Sun.COM 		 * We must have this setup properly for rx buffer
2414*9999SWang.Lin@Sun.COM 		 * DMA to work so force a reasonable value here if it
2415*9999SWang.Lin@Sun.COM 		 * comes up zero.
2416*9999SWang.Lin@Sun.COM 		 */
2417*9999SWang.Lin@Sun.COM 		csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t);
2418*9999SWang.Lin@Sun.COM 		pci_config_put8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ,
2419*9999SWang.Lin@Sun.COM 		    csz);
2420*9999SWang.Lin@Sun.COM 	}
2421*9999SWang.Lin@Sun.COM 	sc->sc_cachelsz = csz << 2;
2422*9999SWang.Lin@Sun.COM }
2423*9999SWang.Lin@Sun.COM 
2424*9999SWang.Lin@Sun.COM static int
2425*9999SWang.Lin@Sun.COM arn_pci_setup(struct arn_softc *sc)
2426*9999SWang.Lin@Sun.COM {
2427*9999SWang.Lin@Sun.COM 	uint16_t command;
2428*9999SWang.Lin@Sun.COM 
2429*9999SWang.Lin@Sun.COM 	/*
2430*9999SWang.Lin@Sun.COM 	 * Enable memory mapping and bus mastering
2431*9999SWang.Lin@Sun.COM 	 */
2432*9999SWang.Lin@Sun.COM 	ASSERT(sc != NULL);
2433*9999SWang.Lin@Sun.COM 	command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM);
2434*9999SWang.Lin@Sun.COM 	command	|= PCI_COMM_MAE | PCI_COMM_ME;
2435*9999SWang.Lin@Sun.COM 	pci_config_put16(sc->sc_cfg_handle, PCI_CONF_COMM, command);
2436*9999SWang.Lin@Sun.COM 	command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM);
2437*9999SWang.Lin@Sun.COM 	if ((command & PCI_COMM_MAE) == 0) {
2438*9999SWang.Lin@Sun.COM 		arn_problem("arn: arn_pci_setup(): "
2439*9999SWang.Lin@Sun.COM 		    "failed to enable memory mapping\n");
2440*9999SWang.Lin@Sun.COM 		return (EIO);
2441*9999SWang.Lin@Sun.COM 	}
2442*9999SWang.Lin@Sun.COM 	if ((command & PCI_COMM_ME) == 0) {
2443*9999SWang.Lin@Sun.COM 		arn_problem("arn: arn_pci_setup(): "
2444*9999SWang.Lin@Sun.COM 		    "failed to enable bus mastering\n");
2445*9999SWang.Lin@Sun.COM 		return (EIO);
2446*9999SWang.Lin@Sun.COM 	}
2447*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: arn_pci_setup(): "
2448*9999SWang.Lin@Sun.COM 	    "set command reg to 0x%x \n", command));
2449*9999SWang.Lin@Sun.COM 
2450*9999SWang.Lin@Sun.COM 	return (0);
2451*9999SWang.Lin@Sun.COM }
2452*9999SWang.Lin@Sun.COM 
2453*9999SWang.Lin@Sun.COM static void
2454*9999SWang.Lin@Sun.COM arn_get_hw_encap(struct arn_softc *sc)
2455*9999SWang.Lin@Sun.COM {
2456*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic;
2457*9999SWang.Lin@Sun.COM 	struct ath_hal *ah;
2458*9999SWang.Lin@Sun.COM 
2459*9999SWang.Lin@Sun.COM 	ic = (ieee80211com_t *)sc;
2460*9999SWang.Lin@Sun.COM 	ah = sc->sc_ah;
2461*9999SWang.Lin@Sun.COM 
2462*9999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2463*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_AES_CCM, NULL))
2464*9999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_AES_CCM;
2465*9999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2466*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_AES_OCB, NULL))
2467*9999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_AES;
2468*9999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2469*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_TKIP, NULL))
2470*9999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_TKIP;
2471*9999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2472*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_WEP, NULL))
2473*9999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_WEP;
2474*9999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2475*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_MIC, NULL))
2476*9999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_TKIPMIC;
2477*9999SWang.Lin@Sun.COM }
2478*9999SWang.Lin@Sun.COM 
2479*9999SWang.Lin@Sun.COM static int
2480*9999SWang.Lin@Sun.COM arn_resume(dev_info_t *devinfo)
2481*9999SWang.Lin@Sun.COM {
2482*9999SWang.Lin@Sun.COM 	struct arn_softc *sc;
2483*9999SWang.Lin@Sun.COM 	int ret = DDI_SUCCESS;
2484*9999SWang.Lin@Sun.COM 
2485*9999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
2486*9999SWang.Lin@Sun.COM 	if (sc == NULL) {
2487*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): "
2488*9999SWang.Lin@Sun.COM 		    "failed to get soft state\n"));
2489*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
2490*9999SWang.Lin@Sun.COM 	}
2491*9999SWang.Lin@Sun.COM 
2492*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2493*9999SWang.Lin@Sun.COM 	/*
2494*9999SWang.Lin@Sun.COM 	 * Set up config space command register(s). Refuse
2495*9999SWang.Lin@Sun.COM 	 * to resume on failure.
2496*9999SWang.Lin@Sun.COM 	 */
2497*9999SWang.Lin@Sun.COM 	if (arn_pci_setup(sc) != 0) {
2498*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): "
2499*9999SWang.Lin@Sun.COM 		    "ath_pci_setup() failed\n"));
2500*9999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
2501*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
2502*9999SWang.Lin@Sun.COM 	}
2503*9999SWang.Lin@Sun.COM 
2504*9999SWang.Lin@Sun.COM 	if (!(sc->sc_flags & SC_OP_INVALID))
2505*9999SWang.Lin@Sun.COM 		ret = arn_open(sc);
2506*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2507*9999SWang.Lin@Sun.COM 
2508*9999SWang.Lin@Sun.COM 	return (ret);
2509*9999SWang.Lin@Sun.COM }
2510*9999SWang.Lin@Sun.COM 
2511*9999SWang.Lin@Sun.COM static int
2512*9999SWang.Lin@Sun.COM arn_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
2513*9999SWang.Lin@Sun.COM {
2514*9999SWang.Lin@Sun.COM 	struct arn_softc *sc;
2515*9999SWang.Lin@Sun.COM 	int		instance;
2516*9999SWang.Lin@Sun.COM 	int		status;
2517*9999SWang.Lin@Sun.COM 	int32_t		err;
2518*9999SWang.Lin@Sun.COM 	uint16_t	vendor_id;
2519*9999SWang.Lin@Sun.COM 	uint16_t	device_id;
2520*9999SWang.Lin@Sun.COM 	uint32_t	i;
2521*9999SWang.Lin@Sun.COM 	uint32_t	val;
2522*9999SWang.Lin@Sun.COM 	char		strbuf[32];
2523*9999SWang.Lin@Sun.COM 	ieee80211com_t *ic;
2524*9999SWang.Lin@Sun.COM 	struct ath_hal *ah;
2525*9999SWang.Lin@Sun.COM 	wifi_data_t wd = { 0 };
2526*9999SWang.Lin@Sun.COM 	mac_register_t *macp;
2527*9999SWang.Lin@Sun.COM 
2528*9999SWang.Lin@Sun.COM 	switch (cmd) {
2529*9999SWang.Lin@Sun.COM 	case DDI_ATTACH:
2530*9999SWang.Lin@Sun.COM 		break;
2531*9999SWang.Lin@Sun.COM 	case DDI_RESUME:
2532*9999SWang.Lin@Sun.COM 		return (arn_resume(devinfo));
2533*9999SWang.Lin@Sun.COM 	default:
2534*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
2535*9999SWang.Lin@Sun.COM 	}
2536*9999SWang.Lin@Sun.COM 
2537*9999SWang.Lin@Sun.COM 	instance = ddi_get_instance(devinfo);
2538*9999SWang.Lin@Sun.COM 	if (ddi_soft_state_zalloc(arn_soft_state_p, instance) != DDI_SUCCESS) {
2539*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: "
2540*9999SWang.Lin@Sun.COM 		    "%s: Unable to alloc softstate\n", __func__));
2541*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
2542*9999SWang.Lin@Sun.COM 	}
2543*9999SWang.Lin@Sun.COM 
2544*9999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
2545*9999SWang.Lin@Sun.COM 	ic = (ieee80211com_t *)sc;
2546*9999SWang.Lin@Sun.COM 	sc->sc_dev = devinfo;
2547*9999SWang.Lin@Sun.COM 
2548*9999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL);
2549*9999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_serial_rw, NULL, MUTEX_DRIVER, NULL);
2550*9999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_txbuflock, NULL, MUTEX_DRIVER, NULL);
2551*9999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_rxbuflock, NULL, MUTEX_DRIVER, NULL);
2552*9999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_resched_lock, NULL, MUTEX_DRIVER, NULL);
2553*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
2554*9999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_bcbuflock, NULL, MUTEX_DRIVER, NULL);
2555*9999SWang.Lin@Sun.COM #endif
2556*9999SWang.Lin@Sun.COM 
2557*9999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
2558*9999SWang.Lin@Sun.COM 
2559*9999SWang.Lin@Sun.COM 	err = pci_config_setup(devinfo, &sc->sc_cfg_handle);
2560*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
2561*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2562*9999SWang.Lin@Sun.COM 		    "pci_config_setup() failed"));
2563*9999SWang.Lin@Sun.COM 		goto attach_fail0;
2564*9999SWang.Lin@Sun.COM 	}
2565*9999SWang.Lin@Sun.COM 
2566*9999SWang.Lin@Sun.COM 	if (arn_pci_setup(sc) != 0)
2567*9999SWang.Lin@Sun.COM 		goto attach_fail1;
2568*9999SWang.Lin@Sun.COM 
2569*9999SWang.Lin@Sun.COM 	/* Cache line size set up */
2570*9999SWang.Lin@Sun.COM 	arn_pci_config_cachesize(sc);
2571*9999SWang.Lin@Sun.COM 
2572*9999SWang.Lin@Sun.COM 	vendor_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_VENID);
2573*9999SWang.Lin@Sun.COM 	device_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_DEVID);
2574*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): vendor 0x%x, "
2575*9999SWang.Lin@Sun.COM 	    "device id 0x%x, cache size %d\n",
2576*9999SWang.Lin@Sun.COM 	    vendor_id, device_id,
2577*9999SWang.Lin@Sun.COM 	    pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ)));
2578*9999SWang.Lin@Sun.COM 
2579*9999SWang.Lin@Sun.COM 	pci_config_put8(sc->sc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8);
2580*9999SWang.Lin@Sun.COM 	val = pci_config_get32(sc->sc_cfg_handle, 0x40);
2581*9999SWang.Lin@Sun.COM 	if ((val & 0x0000ff00) != 0)
2582*9999SWang.Lin@Sun.COM 		pci_config_put32(sc->sc_cfg_handle, 0x40, val & 0xffff00ff);
2583*9999SWang.Lin@Sun.COM 
2584*9999SWang.Lin@Sun.COM 	err = ddi_regs_map_setup(devinfo, 1,
2585*9999SWang.Lin@Sun.COM 	    &sc->mem, 0, 0, &arn_reg_accattr, &sc->sc_io_handle);
2586*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2587*9999SWang.Lin@Sun.COM 	    "regs map1 = %x err=%d\n", sc->mem, err));
2588*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
2589*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2590*9999SWang.Lin@Sun.COM 		    "ddi_regs_map_setup() failed"));
2591*9999SWang.Lin@Sun.COM 		goto attach_fail1;
2592*9999SWang.Lin@Sun.COM 	}
2593*9999SWang.Lin@Sun.COM 
2594*9999SWang.Lin@Sun.COM 	ah = ath9k_hw_attach(device_id, sc, sc->mem, &status);
2595*9999SWang.Lin@Sun.COM 	if (ah == NULL) {
2596*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2597*9999SWang.Lin@Sun.COM 		    "unable to attach hw: H/W status %u\n",
2598*9999SWang.Lin@Sun.COM 		    status));
2599*9999SWang.Lin@Sun.COM 		goto attach_fail2;
2600*9999SWang.Lin@Sun.COM 	}
2601*9999SWang.Lin@Sun.COM 	sc->sc_ah = ah;
2602*9999SWang.Lin@Sun.COM 
2603*9999SWang.Lin@Sun.COM 	ath9k_hw_getmac(ah, ic->ic_macaddr);
2604*9999SWang.Lin@Sun.COM 
2605*9999SWang.Lin@Sun.COM 	/* Get the hardware key cache size. */
2606*9999SWang.Lin@Sun.COM 	sc->sc_keymax = ah->ah_caps.keycache_size;
2607*9999SWang.Lin@Sun.COM 	if (sc->sc_keymax > ATH_KEYMAX) {
2608*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2609*9999SWang.Lin@Sun.COM 		    "Warning, using only %u entries in %u key cache\n",
2610*9999SWang.Lin@Sun.COM 		    ATH_KEYMAX, sc->sc_keymax));
2611*9999SWang.Lin@Sun.COM 		sc->sc_keymax = ATH_KEYMAX;
2612*9999SWang.Lin@Sun.COM 	}
2613*9999SWang.Lin@Sun.COM 
2614*9999SWang.Lin@Sun.COM 	/*
2615*9999SWang.Lin@Sun.COM 	 * Reset the key cache since some parts do not
2616*9999SWang.Lin@Sun.COM 	 * reset the contents on initial power up.
2617*9999SWang.Lin@Sun.COM 	 */
2618*9999SWang.Lin@Sun.COM 	for (i = 0; i < sc->sc_keymax; i++)
2619*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_keyreset(ah, (uint16_t)i);
2620*9999SWang.Lin@Sun.COM 	/*
2621*9999SWang.Lin@Sun.COM 	 * Mark key cache slots associated with global keys
2622*9999SWang.Lin@Sun.COM 	 * as in use.  If we knew TKIP was not to be used we
2623*9999SWang.Lin@Sun.COM 	 * could leave the +32, +64, and +32+64 slots free.
2624*9999SWang.Lin@Sun.COM 	 * XXX only for splitmic.
2625*9999SWang.Lin@Sun.COM 	 */
2626*9999SWang.Lin@Sun.COM 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
2627*9999SWang.Lin@Sun.COM 		set_bit(i, sc->sc_keymap);
2628*9999SWang.Lin@Sun.COM 		set_bit(i + 32, sc->sc_keymap);
2629*9999SWang.Lin@Sun.COM 		set_bit(i + 64, sc->sc_keymap);
2630*9999SWang.Lin@Sun.COM 		set_bit(i + 32 + 64, sc->sc_keymap);
2631*9999SWang.Lin@Sun.COM 	}
2632*9999SWang.Lin@Sun.COM 
2633*9999SWang.Lin@Sun.COM 	/* Collect the channel list using the default country code */
2634*9999SWang.Lin@Sun.COM 	err = arn_setup_channels(sc);
2635*9999SWang.Lin@Sun.COM 	if (err == EINVAL) {
2636*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2637*9999SWang.Lin@Sun.COM 		    "ERR:arn_setup_channels\n"));
2638*9999SWang.Lin@Sun.COM 		goto attach_fail3;
2639*9999SWang.Lin@Sun.COM 	}
2640*9999SWang.Lin@Sun.COM 
2641*9999SWang.Lin@Sun.COM 	/* default to STA mode */
2642*9999SWang.Lin@Sun.COM 	sc->sc_ah->ah_opmode = ATH9K_M_STA;
2643*9999SWang.Lin@Sun.COM 
2644*9999SWang.Lin@Sun.COM 	/* Setup rate tables */
2645*9999SWang.Lin@Sun.COM 	arn_rate_attach(sc);
2646*9999SWang.Lin@Sun.COM 	arn_setup_rates(sc, IEEE80211_MODE_11A);
2647*9999SWang.Lin@Sun.COM 	arn_setup_rates(sc, IEEE80211_MODE_11B);
2648*9999SWang.Lin@Sun.COM 	arn_setup_rates(sc, IEEE80211_MODE_11G);
2649*9999SWang.Lin@Sun.COM 
2650*9999SWang.Lin@Sun.COM 	/* Setup current mode here */
2651*9999SWang.Lin@Sun.COM 	arn_setcurmode(sc, ATH9K_MODE_11G);
2652*9999SWang.Lin@Sun.COM 
2653*9999SWang.Lin@Sun.COM 	/* 802.11g features */
2654*9999SWang.Lin@Sun.COM 	if (sc->sc_have11g)
2655*9999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
2656*9999SWang.Lin@Sun.COM 		    IEEE80211_C_SHSLOT;		/* short slot time */
2657*9999SWang.Lin@Sun.COM 
2658*9999SWang.Lin@Sun.COM 	/* temp workaround */
2659*9999SWang.Lin@Sun.COM 	sc->sc_mrretry = 1;
2660*9999SWang.Lin@Sun.COM 
2661*9999SWang.Lin@Sun.COM 	/* Setup tx/rx descriptors */
2662*9999SWang.Lin@Sun.COM 	err = arn_desc_alloc(devinfo, sc);
2663*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
2664*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2665*9999SWang.Lin@Sun.COM 		    "failed to allocate descriptors: %d\n", err));
2666*9999SWang.Lin@Sun.COM 		goto attach_fail3;
2667*9999SWang.Lin@Sun.COM 	}
2668*9999SWang.Lin@Sun.COM 
2669*9999SWang.Lin@Sun.COM 	if ((sc->sc_tq = ddi_taskq_create(devinfo, "ath_taskq", 1,
2670*9999SWang.Lin@Sun.COM 	    TASKQ_DEFAULTPRI, 0)) == NULL) {
2671*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2672*9999SWang.Lin@Sun.COM 		    "ERR:ddi_taskq_create\n"));
2673*9999SWang.Lin@Sun.COM 		goto attach_fail4;
2674*9999SWang.Lin@Sun.COM 	}
2675*9999SWang.Lin@Sun.COM 
2676*9999SWang.Lin@Sun.COM 	/*
2677*9999SWang.Lin@Sun.COM 	 * Allocate hardware transmit queues: one queue for
2678*9999SWang.Lin@Sun.COM 	 * beacon frames and one data queue for each QoS
2679*9999SWang.Lin@Sun.COM 	 * priority.  Note that the hal handles reseting
2680*9999SWang.Lin@Sun.COM 	 * these queues at the needed time.
2681*9999SWang.Lin@Sun.COM 	 */
2682*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
2683*9999SWang.Lin@Sun.COM 	sc->sc_beaconq = arn_beaconq_setup(ah);
2684*9999SWang.Lin@Sun.COM 	if (sc->sc_beaconq == (-1)) {
2685*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2686*9999SWang.Lin@Sun.COM 		    "unable to setup a beacon xmit queue\n"));
2687*9999SWang.Lin@Sun.COM 		goto attach_fail4;
2688*9999SWang.Lin@Sun.COM 	}
2689*9999SWang.Lin@Sun.COM #endif
2690*9999SWang.Lin@Sun.COM #ifdef ARN_HOSTAP
2691*9999SWang.Lin@Sun.COM 	sc->sc_cabq = arn_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
2692*9999SWang.Lin@Sun.COM 	if (sc->sc_cabq == NULL) {
2693*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2694*9999SWang.Lin@Sun.COM 		    "unable to setup CAB xmit queue\n"));
2695*9999SWang.Lin@Sun.COM 		goto attach_fail4;
2696*9999SWang.Lin@Sun.COM 	}
2697*9999SWang.Lin@Sun.COM 
2698*9999SWang.Lin@Sun.COM 	sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME;
2699*9999SWang.Lin@Sun.COM 	ath_cabq_update(sc);
2700*9999SWang.Lin@Sun.COM #endif
2701*9999SWang.Lin@Sun.COM 
2702*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++)
2703*9999SWang.Lin@Sun.COM 		sc->sc_haltype2q[i] = -1;
2704*9999SWang.Lin@Sun.COM 
2705*9999SWang.Lin@Sun.COM 	/* Setup data queues */
2706*9999SWang.Lin@Sun.COM 	/* NB: ensure BK queue is the lowest priority h/w queue */
2707*9999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_BK)) {
2708*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2709*9999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for BK traffic\n"));
2710*9999SWang.Lin@Sun.COM 		goto attach_fail4;
2711*9999SWang.Lin@Sun.COM 	}
2712*9999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_BE)) {
2713*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2714*9999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for BE traffic\n"));
2715*9999SWang.Lin@Sun.COM 		goto attach_fail4;
2716*9999SWang.Lin@Sun.COM 	}
2717*9999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_VI)) {
2718*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2719*9999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for VI traffic\n"));
2720*9999SWang.Lin@Sun.COM 		goto attach_fail4;
2721*9999SWang.Lin@Sun.COM 	}
2722*9999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_VO)) {
2723*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2724*9999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for VO traffic\n"));
2725*9999SWang.Lin@Sun.COM 		goto attach_fail4;
2726*9999SWang.Lin@Sun.COM 	}
2727*9999SWang.Lin@Sun.COM 
2728*9999SWang.Lin@Sun.COM 	/*
2729*9999SWang.Lin@Sun.COM 	 * Initializes the noise floor to a reasonable default value.
2730*9999SWang.Lin@Sun.COM 	 * Later on this will be updated during ANI processing.
2731*9999SWang.Lin@Sun.COM 	 */
2732*9999SWang.Lin@Sun.COM 
2733*9999SWang.Lin@Sun.COM 	sc->sc_ani.sc_noise_floor = ATH_DEFAULT_NOISE_FLOOR;
2734*9999SWang.Lin@Sun.COM 
2735*9999SWang.Lin@Sun.COM 
2736*9999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2737*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_TKIP, NULL)) {
2738*9999SWang.Lin@Sun.COM 		/*
2739*9999SWang.Lin@Sun.COM 		 * Whether we should enable h/w TKIP MIC.
2740*9999SWang.Lin@Sun.COM 		 * XXX: if we don't support WME TKIP MIC, then we wouldn't
2741*9999SWang.Lin@Sun.COM 		 * report WMM capable, so it's always safe to turn on
2742*9999SWang.Lin@Sun.COM 		 * TKIP MIC in this case.
2743*9999SWang.Lin@Sun.COM 		 */
2744*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
2745*9999SWang.Lin@Sun.COM 		    0, 1, NULL);
2746*9999SWang.Lin@Sun.COM 	}
2747*9999SWang.Lin@Sun.COM 
2748*9999SWang.Lin@Sun.COM 	/* Get cipher releated capability information */
2749*9999SWang.Lin@Sun.COM 	arn_get_hw_encap(sc);
2750*9999SWang.Lin@Sun.COM 
2751*9999SWang.Lin@Sun.COM 	/*
2752*9999SWang.Lin@Sun.COM 	 * Check whether the separate key cache entries
2753*9999SWang.Lin@Sun.COM 	 * are required to handle both tx+rx MIC keys.
2754*9999SWang.Lin@Sun.COM 	 * With split mic keys the number of stations is limited
2755*9999SWang.Lin@Sun.COM 	 * to 27 otherwise 59.
2756*9999SWang.Lin@Sun.COM 	 */
2757*9999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2758*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_TKIP, NULL) &&
2759*9999SWang.Lin@Sun.COM 	    ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
2760*9999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_MIC, NULL) &&
2761*9999SWang.Lin@Sun.COM 	    ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
2762*9999SWang.Lin@Sun.COM 	    0, NULL))
2763*9999SWang.Lin@Sun.COM 		sc->sc_splitmic = 1;
2764*9999SWang.Lin@Sun.COM 
2765*9999SWang.Lin@Sun.COM 	/* turn on mcast key search if possible */
2766*9999SWang.Lin@Sun.COM 	if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
2767*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
2768*9999SWang.Lin@Sun.COM 		    1, NULL);
2769*9999SWang.Lin@Sun.COM 
2770*9999SWang.Lin@Sun.COM 	sc->sc_config.txpowlimit = ATH_TXPOWER_MAX;
2771*9999SWang.Lin@Sun.COM 	sc->sc_config.txpowlimit_override = 0;
2772*9999SWang.Lin@Sun.COM 
2773*9999SWang.Lin@Sun.COM #ifdef ARN_11N
2774*9999SWang.Lin@Sun.COM 	/* 11n Capabilities */
2775*9999SWang.Lin@Sun.COM 	if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
2776*9999SWang.Lin@Sun.COM 		sc->sc_flags |= SC_OP_TXAGGR;
2777*9999SWang.Lin@Sun.COM 		sc->sc_flags |= SC_OP_RXAGGR;
2778*9999SWang.Lin@Sun.COM 	}
2779*9999SWang.Lin@Sun.COM #endif
2780*9999SWang.Lin@Sun.COM 
2781*9999SWang.Lin@Sun.COM #ifdef ARN_11N
2782*9999SWang.Lin@Sun.COM 	sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask;
2783*9999SWang.Lin@Sun.COM 	sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask;
2784*9999SWang.Lin@Sun.COM #else
2785*9999SWang.Lin@Sun.COM 	sc->sc_tx_chainmask = 1;
2786*9999SWang.Lin@Sun.COM 	sc->sc_rx_chainmask = 1;
2787*9999SWang.Lin@Sun.COM #endif
2788*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2789*9999SWang.Lin@Sun.COM 	    "tx_chainmask = %d, rx_chainmask = %d\n",
2790*9999SWang.Lin@Sun.COM 	    sc->sc_tx_chainmask, sc->sc_rx_chainmask));
2791*9999SWang.Lin@Sun.COM 
2792*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, B_TRUE, NULL);
2793*9999SWang.Lin@Sun.COM 	sc->sc_defant = ath9k_hw_getdefantenna(ah);
2794*9999SWang.Lin@Sun.COM 
2795*9999SWang.Lin@Sun.COM 	ath9k_hw_getmac(ah, sc->sc_myaddr);
2796*9999SWang.Lin@Sun.COM 	if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
2797*9999SWang.Lin@Sun.COM 		ath9k_hw_getbssidmask(ah, sc->sc_bssidmask);
2798*9999SWang.Lin@Sun.COM 		ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask);
2799*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_setbssidmask(ah, sc->sc_bssidmask);
2800*9999SWang.Lin@Sun.COM 	}
2801*9999SWang.Lin@Sun.COM 
2802*9999SWang.Lin@Sun.COM 	/* set default value to short slot time */
2803*9999SWang.Lin@Sun.COM 	sc->sc_slottime = ATH9K_SLOT_TIME_9;
2804*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9);
2805*9999SWang.Lin@Sun.COM 
2806*9999SWang.Lin@Sun.COM 	/* initialize beacon slots */
2807*9999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++)
2808*9999SWang.Lin@Sun.COM 		sc->sc_bslot[i] = ATH_IF_ID_ANY;
2809*9999SWang.Lin@Sun.COM 
2810*9999SWang.Lin@Sun.COM 	/* save MISC configurations */
2811*9999SWang.Lin@Sun.COM 	sc->sc_config.swBeaconProcess = 1;
2812*9999SWang.Lin@Sun.COM 
2813*9999SWang.Lin@Sun.COM 
2814*9999SWang.Lin@Sun.COM 	ic->ic_caps |= IEEE80211_C_WPA;	/* Support WPA/WPA2 */
2815*9999SWang.Lin@Sun.COM 	ic->ic_phytype = IEEE80211_T_OFDM;
2816*9999SWang.Lin@Sun.COM 	ic->ic_opmode = IEEE80211_M_STA;
2817*9999SWang.Lin@Sun.COM 	ic->ic_state = IEEE80211_S_INIT;
2818*9999SWang.Lin@Sun.COM 	ic->ic_maxrssi = ARN_MAX_RSSI;
2819*9999SWang.Lin@Sun.COM 	ic->ic_set_shortslot = arn_set_shortslot;
2820*9999SWang.Lin@Sun.COM 	ic->ic_xmit = arn_tx;
2821*9999SWang.Lin@Sun.COM 	ieee80211_attach(ic);
2822*9999SWang.Lin@Sun.COM 
2823*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2824*9999SWang.Lin@Sun.COM 	    "ic->ic_curchan->ich_freq: %d\n", ic->ic_curchan->ich_freq));
2825*9999SWang.Lin@Sun.COM 
2826*9999SWang.Lin@Sun.COM 	/* different instance has different WPA door */
2827*9999SWang.Lin@Sun.COM 	(void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR,
2828*9999SWang.Lin@Sun.COM 	    ddi_driver_name(devinfo),
2829*9999SWang.Lin@Sun.COM 	    ddi_get_instance(devinfo));
2830*9999SWang.Lin@Sun.COM 
2831*9999SWang.Lin@Sun.COM 	/* Override 80211 default routines */
2832*9999SWang.Lin@Sun.COM 	ic->ic_reset = arn_reset;
2833*9999SWang.Lin@Sun.COM 	sc->sc_newstate = ic->ic_newstate;
2834*9999SWang.Lin@Sun.COM 	ic->ic_newstate = arn_newstate;
2835*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
2836*9999SWang.Lin@Sun.COM 	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
2837*9999SWang.Lin@Sun.COM 	ic->ic_recv_mgmt = arn_recv_mgmt;
2838*9999SWang.Lin@Sun.COM #endif
2839*9999SWang.Lin@Sun.COM 	ic->ic_watchdog = arn_watchdog;
2840*9999SWang.Lin@Sun.COM 	ic->ic_node_alloc = arn_node_alloc;
2841*9999SWang.Lin@Sun.COM 	ic->ic_node_free = arn_node_free;
2842*9999SWang.Lin@Sun.COM 	ic->ic_crypto.cs_key_alloc = arn_key_alloc;
2843*9999SWang.Lin@Sun.COM 	ic->ic_crypto.cs_key_delete = arn_key_delete;
2844*9999SWang.Lin@Sun.COM 	ic->ic_crypto.cs_key_set = arn_key_set;
2845*9999SWang.Lin@Sun.COM 
2846*9999SWang.Lin@Sun.COM 	ieee80211_media_init(ic);
2847*9999SWang.Lin@Sun.COM 
2848*9999SWang.Lin@Sun.COM 	/*
2849*9999SWang.Lin@Sun.COM 	 * initialize default tx key
2850*9999SWang.Lin@Sun.COM 	 */
2851*9999SWang.Lin@Sun.COM 	ic->ic_def_txkey = 0;
2852*9999SWang.Lin@Sun.COM 
2853*9999SWang.Lin@Sun.COM 	sc->sc_rx_pend = 0;
2854*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
2855*9999SWang.Lin@Sun.COM 	err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW,
2856*9999SWang.Lin@Sun.COM 	    &sc->sc_softint_id, NULL, 0, arn_softint_handler, (caddr_t)sc);
2857*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
2858*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2859*9999SWang.Lin@Sun.COM 		    "ddi_add_softintr() failed....\n"));
2860*9999SWang.Lin@Sun.COM 		goto attach_fail5;
2861*9999SWang.Lin@Sun.COM 	}
2862*9999SWang.Lin@Sun.COM 
2863*9999SWang.Lin@Sun.COM 	if (ddi_get_iblock_cookie(devinfo, 0, &sc->sc_iblock)
2864*9999SWang.Lin@Sun.COM 	    != DDI_SUCCESS) {
2865*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2866*9999SWang.Lin@Sun.COM 		    "Can not get iblock cookie for INT\n"));
2867*9999SWang.Lin@Sun.COM 		goto attach_fail6;
2868*9999SWang.Lin@Sun.COM 	}
2869*9999SWang.Lin@Sun.COM 
2870*9999SWang.Lin@Sun.COM 	if (ddi_add_intr(devinfo, 0, NULL, NULL, arn_isr,
2871*9999SWang.Lin@Sun.COM 	    (caddr_t)sc) != DDI_SUCCESS) {
2872*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2873*9999SWang.Lin@Sun.COM 		    "Can not set intr for ARN driver\n"));
2874*9999SWang.Lin@Sun.COM 		goto attach_fail6;
2875*9999SWang.Lin@Sun.COM 	}
2876*9999SWang.Lin@Sun.COM 
2877*9999SWang.Lin@Sun.COM 	/*
2878*9999SWang.Lin@Sun.COM 	 * Provide initial settings for the WiFi plugin; whenever this
2879*9999SWang.Lin@Sun.COM 	 * information changes, we need to call mac_plugindata_update()
2880*9999SWang.Lin@Sun.COM 	 */
2881*9999SWang.Lin@Sun.COM 	wd.wd_opmode = ic->ic_opmode;
2882*9999SWang.Lin@Sun.COM 	wd.wd_secalloc = WIFI_SEC_NONE;
2883*9999SWang.Lin@Sun.COM 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
2884*9999SWang.Lin@Sun.COM 
2885*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2886*9999SWang.Lin@Sun.COM 	    "IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid)"
2887*9999SWang.Lin@Sun.COM 	    "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
2888*9999SWang.Lin@Sun.COM 	    wd.wd_bssid[0], wd.wd_bssid[1], wd.wd_bssid[2],
2889*9999SWang.Lin@Sun.COM 	    wd.wd_bssid[3], wd.wd_bssid[4], wd.wd_bssid[5]));
2890*9999SWang.Lin@Sun.COM 
2891*9999SWang.Lin@Sun.COM 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
2892*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2893*9999SWang.Lin@Sun.COM 		    "MAC version mismatch\n"));
2894*9999SWang.Lin@Sun.COM 		goto attach_fail7;
2895*9999SWang.Lin@Sun.COM 	}
2896*9999SWang.Lin@Sun.COM 
2897*9999SWang.Lin@Sun.COM 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
2898*9999SWang.Lin@Sun.COM 	macp->m_driver		= sc;
2899*9999SWang.Lin@Sun.COM 	macp->m_dip		= devinfo;
2900*9999SWang.Lin@Sun.COM 	macp->m_src_addr	= ic->ic_macaddr;
2901*9999SWang.Lin@Sun.COM 	macp->m_callbacks	= &arn_m_callbacks;
2902*9999SWang.Lin@Sun.COM 	macp->m_min_sdu		= 0;
2903*9999SWang.Lin@Sun.COM 	macp->m_max_sdu		= IEEE80211_MTU;
2904*9999SWang.Lin@Sun.COM 	macp->m_pdata		= &wd;
2905*9999SWang.Lin@Sun.COM 	macp->m_pdata_size	= sizeof (wd);
2906*9999SWang.Lin@Sun.COM 
2907*9999SWang.Lin@Sun.COM 	err = mac_register(macp, &ic->ic_mach);
2908*9999SWang.Lin@Sun.COM 	mac_free(macp);
2909*9999SWang.Lin@Sun.COM 	if (err != 0) {
2910*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2911*9999SWang.Lin@Sun.COM 		    "mac_register err %x\n", err));
2912*9999SWang.Lin@Sun.COM 		goto attach_fail7;
2913*9999SWang.Lin@Sun.COM 	}
2914*9999SWang.Lin@Sun.COM 
2915*9999SWang.Lin@Sun.COM 	/* Create minor node of type DDI_NT_NET_WIFI */
2916*9999SWang.Lin@Sun.COM 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
2917*9999SWang.Lin@Sun.COM 	    ARN_NODENAME, instance);
2918*9999SWang.Lin@Sun.COM 	err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
2919*9999SWang.Lin@Sun.COM 	    instance + 1, DDI_NT_NET_WIFI, 0);
2920*9999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS)
2921*9999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "WARN: arn: arn_attach(): "
2922*9999SWang.Lin@Sun.COM 		    "Create minor node failed - %d\n", err));
2923*9999SWang.Lin@Sun.COM 
2924*9999SWang.Lin@Sun.COM 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
2925*9999SWang.Lin@Sun.COM 
2926*9999SWang.Lin@Sun.COM 	sc->sc_promisc = B_FALSE;
2927*9999SWang.Lin@Sun.COM 	bzero(sc->sc_mcast_refs, sizeof (sc->sc_mcast_refs));
2928*9999SWang.Lin@Sun.COM 	bzero(sc->sc_mcast_hash, sizeof (sc->sc_mcast_hash));
2929*9999SWang.Lin@Sun.COM 
2930*9999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
2931*9999SWang.Lin@Sun.COM 	    "Atheros AR%s MAC/BB Rev:%x "
2932*9999SWang.Lin@Sun.COM 	    "AR%s RF Rev:%x: mem=0x%lx\n",
2933*9999SWang.Lin@Sun.COM 	    arn_mac_bb_name(ah->ah_macVersion),
2934*9999SWang.Lin@Sun.COM 	    ah->ah_macRev,
2935*9999SWang.Lin@Sun.COM 	    arn_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)),
2936*9999SWang.Lin@Sun.COM 	    ah->ah_phyRev,
2937*9999SWang.Lin@Sun.COM 	    (unsigned long)sc->mem));
2938*9999SWang.Lin@Sun.COM 
2939*9999SWang.Lin@Sun.COM 	/* XXX: hardware will not be ready until arn_open() being called */
2940*9999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
2941*9999SWang.Lin@Sun.COM 	sc->sc_isrunning = 0;
2942*9999SWang.Lin@Sun.COM 
2943*9999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
2944*9999SWang.Lin@Sun.COM 
2945*9999SWang.Lin@Sun.COM attach_fail7:
2946*9999SWang.Lin@Sun.COM 	ddi_remove_intr(devinfo, 0, sc->sc_iblock);
2947*9999SWang.Lin@Sun.COM attach_fail6:
2948*9999SWang.Lin@Sun.COM 	ddi_remove_softintr(sc->sc_softint_id);
2949*9999SWang.Lin@Sun.COM attach_fail5:
2950*9999SWang.Lin@Sun.COM 	(void) ieee80211_detach(ic);
2951*9999SWang.Lin@Sun.COM attach_fail4:
2952*9999SWang.Lin@Sun.COM 	arn_desc_free(sc);
2953*9999SWang.Lin@Sun.COM 	if (sc->sc_tq)
2954*9999SWang.Lin@Sun.COM 		ddi_taskq_destroy(sc->sc_tq);
2955*9999SWang.Lin@Sun.COM attach_fail3:
2956*9999SWang.Lin@Sun.COM 	ath9k_hw_detach(ah);
2957*9999SWang.Lin@Sun.COM attach_fail2:
2958*9999SWang.Lin@Sun.COM 	ddi_regs_map_free(&sc->sc_io_handle);
2959*9999SWang.Lin@Sun.COM attach_fail1:
2960*9999SWang.Lin@Sun.COM 	pci_config_teardown(&sc->sc_cfg_handle);
2961*9999SWang.Lin@Sun.COM attach_fail0:
2962*9999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
2963*9999SWang.Lin@Sun.COM 	/* cleanup tx queues */
2964*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_txbuflock);
2965*9999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
2966*9999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i)) {
2967*9999SWang.Lin@Sun.COM 			/* arn_tx_cleanupq(asc, &asc->sc_txq[i]); */
2968*9999SWang.Lin@Sun.COM 			mutex_destroy(&((&sc->sc_txq[i])->axq_lock));
2969*9999SWang.Lin@Sun.COM 		}
2970*9999SWang.Lin@Sun.COM 	}
2971*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_rxbuflock);
2972*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_serial_rw);
2973*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_genlock);
2974*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_resched_lock);
2975*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
2976*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_bcbuflock);
2977*9999SWang.Lin@Sun.COM #endif
2978*9999SWang.Lin@Sun.COM 
2979*9999SWang.Lin@Sun.COM 	ddi_soft_state_free(arn_soft_state_p, instance);
2980*9999SWang.Lin@Sun.COM 
2981*9999SWang.Lin@Sun.COM 	return (DDI_FAILURE);
2982*9999SWang.Lin@Sun.COM 
2983*9999SWang.Lin@Sun.COM }
2984*9999SWang.Lin@Sun.COM 
2985*9999SWang.Lin@Sun.COM /*
2986*9999SWang.Lin@Sun.COM  * Suspend transmit/receive for powerdown
2987*9999SWang.Lin@Sun.COM  */
2988*9999SWang.Lin@Sun.COM static int
2989*9999SWang.Lin@Sun.COM arn_suspend(struct arn_softc *sc)
2990*9999SWang.Lin@Sun.COM {
2991*9999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
2992*9999SWang.Lin@Sun.COM 	arn_close(sc);
2993*9999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
2994*9999SWang.Lin@Sun.COM 
2995*9999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
2996*9999SWang.Lin@Sun.COM }
2997*9999SWang.Lin@Sun.COM 
2998*9999SWang.Lin@Sun.COM static int32_t
2999*9999SWang.Lin@Sun.COM arn_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
3000*9999SWang.Lin@Sun.COM {
3001*9999SWang.Lin@Sun.COM 	struct arn_softc *sc;
3002*9999SWang.Lin@Sun.COM 	int i;
3003*9999SWang.Lin@Sun.COM 
3004*9999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
3005*9999SWang.Lin@Sun.COM 	ASSERT(sc != NULL);
3006*9999SWang.Lin@Sun.COM 
3007*9999SWang.Lin@Sun.COM 	switch (cmd) {
3008*9999SWang.Lin@Sun.COM 	case DDI_DETACH:
3009*9999SWang.Lin@Sun.COM 		break;
3010*9999SWang.Lin@Sun.COM 
3011*9999SWang.Lin@Sun.COM 	case DDI_SUSPEND:
3012*9999SWang.Lin@Sun.COM 		return (arn_suspend(sc));
3013*9999SWang.Lin@Sun.COM 
3014*9999SWang.Lin@Sun.COM 	default:
3015*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
3016*9999SWang.Lin@Sun.COM 	}
3017*9999SWang.Lin@Sun.COM 
3018*9999SWang.Lin@Sun.COM 	if (mac_disable(sc->sc_isc.ic_mach) != 0)
3019*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
3020*9999SWang.Lin@Sun.COM 
3021*9999SWang.Lin@Sun.COM 	arn_stop_scantimer(sc);
3022*9999SWang.Lin@Sun.COM 	arn_stop_caltimer(sc);
3023*9999SWang.Lin@Sun.COM 
3024*9999SWang.Lin@Sun.COM 	/* disable interrupts */
3025*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
3026*9999SWang.Lin@Sun.COM 
3027*9999SWang.Lin@Sun.COM 	/*
3028*9999SWang.Lin@Sun.COM 	 * Unregister from the MAC layer subsystem
3029*9999SWang.Lin@Sun.COM 	 */
3030*9999SWang.Lin@Sun.COM 	(void) mac_unregister(sc->sc_isc.ic_mach);
3031*9999SWang.Lin@Sun.COM 
3032*9999SWang.Lin@Sun.COM 	/* free intterrupt resources */
3033*9999SWang.Lin@Sun.COM 	ddi_remove_intr(devinfo, 0, sc->sc_iblock);
3034*9999SWang.Lin@Sun.COM 	ddi_remove_softintr(sc->sc_softint_id);
3035*9999SWang.Lin@Sun.COM 
3036*9999SWang.Lin@Sun.COM 	/*
3037*9999SWang.Lin@Sun.COM 	 * NB: the order of these is important:
3038*9999SWang.Lin@Sun.COM 	 * o call the 802.11 layer before detaching the hal to
3039*9999SWang.Lin@Sun.COM 	 *   insure callbacks into the driver to delete global
3040*9999SWang.Lin@Sun.COM 	 *   key cache entries can be handled
3041*9999SWang.Lin@Sun.COM 	 * o reclaim the tx queue data structures after calling
3042*9999SWang.Lin@Sun.COM 	 *   the 802.11 layer as we'll get called back to reclaim
3043*9999SWang.Lin@Sun.COM 	 *   node state and potentially want to use them
3044*9999SWang.Lin@Sun.COM 	 * o to cleanup the tx queues the hal is called, so detach
3045*9999SWang.Lin@Sun.COM 	 *   it last
3046*9999SWang.Lin@Sun.COM 	 */
3047*9999SWang.Lin@Sun.COM 	ieee80211_detach(&sc->sc_isc);
3048*9999SWang.Lin@Sun.COM 
3049*9999SWang.Lin@Sun.COM 	arn_desc_free(sc);
3050*9999SWang.Lin@Sun.COM 
3051*9999SWang.Lin@Sun.COM 	ddi_taskq_destroy(sc->sc_tq);
3052*9999SWang.Lin@Sun.COM 
3053*9999SWang.Lin@Sun.COM 	if (!(sc->sc_flags & SC_OP_INVALID))
3054*9999SWang.Lin@Sun.COM 		(void) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
3055*9999SWang.Lin@Sun.COM 
3056*9999SWang.Lin@Sun.COM 	/* cleanup tx queues */
3057*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_txbuflock);
3058*9999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
3059*9999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i)) {
3060*9999SWang.Lin@Sun.COM 			arn_tx_cleanupq(sc, &sc->sc_txq[i]);
3061*9999SWang.Lin@Sun.COM 			mutex_destroy(&((&sc->sc_txq[i])->axq_lock));
3062*9999SWang.Lin@Sun.COM 		}
3063*9999SWang.Lin@Sun.COM 	}
3064*9999SWang.Lin@Sun.COM 
3065*9999SWang.Lin@Sun.COM 	ath9k_hw_detach(sc->sc_ah);
3066*9999SWang.Lin@Sun.COM 
3067*9999SWang.Lin@Sun.COM 	/* free io handle */
3068*9999SWang.Lin@Sun.COM 	ddi_regs_map_free(&sc->sc_io_handle);
3069*9999SWang.Lin@Sun.COM 	pci_config_teardown(&sc->sc_cfg_handle);
3070*9999SWang.Lin@Sun.COM 
3071*9999SWang.Lin@Sun.COM 	/* destroy locks */
3072*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_genlock);
3073*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_serial_rw);
3074*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_rxbuflock);
3075*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_resched_lock);
3076*9999SWang.Lin@Sun.COM #ifdef ARN_IBSS
3077*9999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_bcbuflock);
3078*9999SWang.Lin@Sun.COM #endif
3079*9999SWang.Lin@Sun.COM 
3080*9999SWang.Lin@Sun.COM 	ddi_remove_minor_node(devinfo, NULL);
3081*9999SWang.Lin@Sun.COM 	ddi_soft_state_free(arn_soft_state_p, ddi_get_instance(devinfo));
3082*9999SWang.Lin@Sun.COM 
3083*9999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
3084*9999SWang.Lin@Sun.COM }
3085*9999SWang.Lin@Sun.COM 
3086*9999SWang.Lin@Sun.COM /*
3087*9999SWang.Lin@Sun.COM  * quiesce(9E) entry point.
3088*9999SWang.Lin@Sun.COM  *
3089*9999SWang.Lin@Sun.COM  * This function is called when the system is single-threaded at high
3090*9999SWang.Lin@Sun.COM  * PIL with preemption disabled. Therefore, this function must not be
3091*9999SWang.Lin@Sun.COM  * blocked.
3092*9999SWang.Lin@Sun.COM  *
3093*9999SWang.Lin@Sun.COM  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
3094*9999SWang.Lin@Sun.COM  * DDI_FAILURE indicates an error condition and should almost never happen.
3095*9999SWang.Lin@Sun.COM  */
3096*9999SWang.Lin@Sun.COM static int32_t
3097*9999SWang.Lin@Sun.COM arn_quiesce(dev_info_t *devinfo)
3098*9999SWang.Lin@Sun.COM {
3099*9999SWang.Lin@Sun.COM 	struct arn_softc *sc;
3100*9999SWang.Lin@Sun.COM 	int i;
3101*9999SWang.Lin@Sun.COM 	struct ath_hal *ah;
3102*9999SWang.Lin@Sun.COM 
3103*9999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
3104*9999SWang.Lin@Sun.COM 
3105*9999SWang.Lin@Sun.COM 	if (sc == NULL || (ah = sc->sc_ah) == NULL)
3106*9999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
3107*9999SWang.Lin@Sun.COM 
3108*9999SWang.Lin@Sun.COM 	/*
3109*9999SWang.Lin@Sun.COM 	 * Disable interrupts
3110*9999SWang.Lin@Sun.COM 	 */
3111*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, 0);
3112*9999SWang.Lin@Sun.COM 
3113*9999SWang.Lin@Sun.COM 	/*
3114*9999SWang.Lin@Sun.COM 	 * Disable TX HW
3115*9999SWang.Lin@Sun.COM 	 */
3116*9999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
3117*9999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i))
3118*9999SWang.Lin@Sun.COM 			(void) ath9k_hw_stoptxdma(ah, sc->sc_txq[i].axq_qnum);
3119*9999SWang.Lin@Sun.COM 	}
3120*9999SWang.Lin@Sun.COM 
3121*9999SWang.Lin@Sun.COM 	/*
3122*9999SWang.Lin@Sun.COM 	 * Disable RX HW
3123*9999SWang.Lin@Sun.COM 	 */
3124*9999SWang.Lin@Sun.COM 	ath9k_hw_stoppcurecv(ah);
3125*9999SWang.Lin@Sun.COM 	ath9k_hw_setrxfilter(ah, 0);
3126*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_stopdmarecv(ah);
3127*9999SWang.Lin@Sun.COM 	drv_usecwait(3000);
3128*9999SWang.Lin@Sun.COM 
3129*9999SWang.Lin@Sun.COM 	/*
3130*9999SWang.Lin@Sun.COM 	 * Power down HW
3131*9999SWang.Lin@Sun.COM 	 */
3132*9999SWang.Lin@Sun.COM 	(void) ath9k_hw_phy_disable(ah);
3133*9999SWang.Lin@Sun.COM 
3134*9999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
3135*9999SWang.Lin@Sun.COM }
3136*9999SWang.Lin@Sun.COM 
3137*9999SWang.Lin@Sun.COM DDI_DEFINE_STREAM_OPS(arn_dev_ops, nulldev, nulldev, arn_attach, arn_detach,
3138*9999SWang.Lin@Sun.COM     nodev, NULL, D_MP, NULL, arn_quiesce);
3139*9999SWang.Lin@Sun.COM 
3140*9999SWang.Lin@Sun.COM static struct modldrv arn_modldrv = {
3141*9999SWang.Lin@Sun.COM 	&mod_driverops, /* Type of module.  This one is a driver */
3142*9999SWang.Lin@Sun.COM 	"arn-Atheros 9000 series driver:vertion 1.1", /* short description */
3143*9999SWang.Lin@Sun.COM 	&arn_dev_ops /* driver specific ops */
3144*9999SWang.Lin@Sun.COM };
3145*9999SWang.Lin@Sun.COM 
3146*9999SWang.Lin@Sun.COM static struct modlinkage modlinkage = {
3147*9999SWang.Lin@Sun.COM 	MODREV_1, (void *)&arn_modldrv, NULL
3148*9999SWang.Lin@Sun.COM };
3149*9999SWang.Lin@Sun.COM 
3150*9999SWang.Lin@Sun.COM int
3151*9999SWang.Lin@Sun.COM _info(struct modinfo *modinfop)
3152*9999SWang.Lin@Sun.COM {
3153*9999SWang.Lin@Sun.COM 	return (mod_info(&modlinkage, modinfop));
3154*9999SWang.Lin@Sun.COM }
3155*9999SWang.Lin@Sun.COM 
3156*9999SWang.Lin@Sun.COM int
3157*9999SWang.Lin@Sun.COM _init(void)
3158*9999SWang.Lin@Sun.COM {
3159*9999SWang.Lin@Sun.COM 	int status;
3160*9999SWang.Lin@Sun.COM 
3161*9999SWang.Lin@Sun.COM 	status = ddi_soft_state_init
3162*9999SWang.Lin@Sun.COM 	    (&arn_soft_state_p, sizeof (struct arn_softc), 1);
3163*9999SWang.Lin@Sun.COM 	if (status != 0)
3164*9999SWang.Lin@Sun.COM 		return (status);
3165*9999SWang.Lin@Sun.COM 
3166*9999SWang.Lin@Sun.COM 	mutex_init(&arn_loglock, NULL, MUTEX_DRIVER, NULL);
3167*9999SWang.Lin@Sun.COM 	mac_init_ops(&arn_dev_ops, "arn");
3168*9999SWang.Lin@Sun.COM 	status = mod_install(&modlinkage);
3169*9999SWang.Lin@Sun.COM 	if (status != 0) {
3170*9999SWang.Lin@Sun.COM 		mac_fini_ops(&arn_dev_ops);
3171*9999SWang.Lin@Sun.COM 		mutex_destroy(&arn_loglock);
3172*9999SWang.Lin@Sun.COM 		ddi_soft_state_fini(&arn_soft_state_p);
3173*9999SWang.Lin@Sun.COM 	}
3174*9999SWang.Lin@Sun.COM 
3175*9999SWang.Lin@Sun.COM 	return (status);
3176*9999SWang.Lin@Sun.COM }
3177*9999SWang.Lin@Sun.COM 
3178*9999SWang.Lin@Sun.COM int
3179*9999SWang.Lin@Sun.COM _fini(void)
3180*9999SWang.Lin@Sun.COM {
3181*9999SWang.Lin@Sun.COM 	int status;
3182*9999SWang.Lin@Sun.COM 
3183*9999SWang.Lin@Sun.COM 	status = mod_remove(&modlinkage);
3184*9999SWang.Lin@Sun.COM 	if (status == 0) {
3185*9999SWang.Lin@Sun.COM 		mac_fini_ops(&arn_dev_ops);
3186*9999SWang.Lin@Sun.COM 		mutex_destroy(&arn_loglock);
3187*9999SWang.Lin@Sun.COM 		ddi_soft_state_fini(&arn_soft_state_p);
3188*9999SWang.Lin@Sun.COM 	}
3189*9999SWang.Lin@Sun.COM 	return (status);
3190*9999SWang.Lin@Sun.COM }
3191