xref: /onnv-gate/usr/src/uts/common/io/ath/ath_aux.c (revision 1000:dd54117d55b1)
1*1000Sxc151355 /*
2*1000Sxc151355  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3*1000Sxc151355  * Use is subject to license terms.
4*1000Sxc151355  */
5*1000Sxc151355 
6*1000Sxc151355 /*
7*1000Sxc151355  * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
8*1000Sxc151355  * All rights reserved.
9*1000Sxc151355  *
10*1000Sxc151355  * Redistribution and use in source and binary forms, with or without
11*1000Sxc151355  * modification, are permitted provided that the following conditions
12*1000Sxc151355  * are met:
13*1000Sxc151355  * 1. Redistributions of source code must retain the above copyright
14*1000Sxc151355  * notice, this list of conditions and the following disclaimer,
15*1000Sxc151355  * without modification.
16*1000Sxc151355  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
17*1000Sxc151355  * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
18*1000Sxc151355  * redistribution must be conditioned upon including a substantially
19*1000Sxc151355  * similar Disclaimer requirement for further binary redistribution.
20*1000Sxc151355  * 3. Neither the names of the above-listed copyright holders nor the names
21*1000Sxc151355  * of any contributors may be used to endorse or promote products derived
22*1000Sxc151355  * from this software without specific prior written permission.
23*1000Sxc151355  *
24*1000Sxc151355  * NO WARRANTY
25*1000Sxc151355  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26*1000Sxc151355  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27*1000Sxc151355  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
28*1000Sxc151355  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
29*1000Sxc151355  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
30*1000Sxc151355  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31*1000Sxc151355  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32*1000Sxc151355  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
33*1000Sxc151355  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34*1000Sxc151355  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35*1000Sxc151355  * THE POSSIBILITY OF SUCH DAMAGES.
36*1000Sxc151355  */
37*1000Sxc151355 
38*1000Sxc151355 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39*1000Sxc151355 
40*1000Sxc151355 #include <sys/param.h>
41*1000Sxc151355 #include <sys/types.h>
42*1000Sxc151355 #include <sys/signal.h>
43*1000Sxc151355 #include <sys/stream.h>
44*1000Sxc151355 #include <sys/termio.h>
45*1000Sxc151355 #include <sys/errno.h>
46*1000Sxc151355 #include <sys/file.h>
47*1000Sxc151355 #include <sys/cmn_err.h>
48*1000Sxc151355 #include <sys/stropts.h>
49*1000Sxc151355 #include <sys/strsubr.h>
50*1000Sxc151355 #include <sys/strtty.h>
51*1000Sxc151355 #include <sys/kbio.h>
52*1000Sxc151355 #include <sys/cred.h>
53*1000Sxc151355 #include <sys/stat.h>
54*1000Sxc151355 #include <sys/consdev.h>
55*1000Sxc151355 #include <sys/kmem.h>
56*1000Sxc151355 #include <sys/modctl.h>
57*1000Sxc151355 #include <sys/ddi.h>
58*1000Sxc151355 #include <sys/sunddi.h>
59*1000Sxc151355 #include <sys/pci.h>
60*1000Sxc151355 #include <sys/errno.h>
61*1000Sxc151355 #include <sys/gld.h>
62*1000Sxc151355 #include <sys/dlpi.h>
63*1000Sxc151355 #include <sys/ethernet.h>
64*1000Sxc151355 #include <sys/list.h>
65*1000Sxc151355 #include <sys/byteorder.h>
66*1000Sxc151355 #include <sys/strsun.h>
67*1000Sxc151355 #include <inet/common.h>
68*1000Sxc151355 #include <inet/nd.h>
69*1000Sxc151355 #include <inet/mi.h>
70*1000Sxc151355 #include <inet/wifi_ioctl.h>
71*1000Sxc151355 #include "ath_hal.h"
72*1000Sxc151355 #include "ath_impl.h"
73*1000Sxc151355 #include "ath_ieee80211.h"
74*1000Sxc151355 
75*1000Sxc151355 static const char *acnames[] = {
76*1000Sxc151355 	"WME_AC_BE",
77*1000Sxc151355 	"WME_AC_BK",
78*1000Sxc151355 	"WME_AC_VI",
79*1000Sxc151355 	"WME_AC_VO",
80*1000Sxc151355 	"WME_UPSD"
81*1000Sxc151355 };
82*1000Sxc151355 
83*1000Sxc151355 extern void ath_setup_desc(ath_t *asc, struct ath_buf *bf);
84*1000Sxc151355 
85*1000Sxc151355 uint32_t
86*1000Sxc151355 ath_calcrxfilter(ath_t *asc)
87*1000Sxc151355 {
88*1000Sxc151355 	ieee80211com_t *isc = (ieee80211com_t *)asc;
89*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
90*1000Sxc151355 	uint32_t rfilt;
91*1000Sxc151355 
92*1000Sxc151355 	rfilt = (ATH_HAL_GETRXFILTER(ah) & HAL_RX_FILTER_PHYERR)
93*1000Sxc151355 	    | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
94*1000Sxc151355 	if (isc->isc_opmode != IEEE80211_M_STA)
95*1000Sxc151355 		rfilt |= HAL_RX_FILTER_PROBEREQ;
96*1000Sxc151355 	if (isc->isc_opmode != IEEE80211_M_HOSTAP &&
97*1000Sxc151355 	    (asc->asc_promisc & GLD_MAC_PROMISC_PHYS))	/* promiscuous */
98*1000Sxc151355 		rfilt |= HAL_RX_FILTER_PROM;
99*1000Sxc151355 	if (isc->isc_opmode == IEEE80211_M_STA ||
100*1000Sxc151355 	    isc->isc_opmode == IEEE80211_M_IBSS ||
101*1000Sxc151355 	    isc->isc_state == IEEE80211_S_SCAN)
102*1000Sxc151355 		rfilt |= HAL_RX_FILTER_BEACON;
103*1000Sxc151355 	return (rfilt);
104*1000Sxc151355 }
105*1000Sxc151355 
106*1000Sxc151355 static int
107*1000Sxc151355 ath_set_data_queue(ath_t *asc, int ac, int haltype)
108*1000Sxc151355 {
109*1000Sxc151355 	HAL_TXQ_INFO qi;
110*1000Sxc151355 	int qnum;
111*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
112*1000Sxc151355 	struct ath_txq *txq;
113*1000Sxc151355 
114*1000Sxc151355 	if (ac >= ATH_N(asc->asc_ac2q)) {
115*1000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
116*1000Sxc151355 		    "ac %u out of range, max %u!\n",
117*1000Sxc151355 		    ac, ATH_N(asc->asc_ac2q)));
118*1000Sxc151355 		return (1);
119*1000Sxc151355 	}
120*1000Sxc151355 	(void) memset(&qi, 0, sizeof (qi));
121*1000Sxc151355 	qi.tqi_subtype = haltype;
122*1000Sxc151355 	/*
123*1000Sxc151355 	 * Enable interrupts only for EOL and DESC conditions.
124*1000Sxc151355 	 * We mark tx descriptors to receive a DESC interrupt
125*1000Sxc151355 	 * when a tx queue gets deep; otherwise waiting for the
126*1000Sxc151355 	 * EOL to reap descriptors.  Note that this is done to
127*1000Sxc151355 	 * reduce interrupt load and this only defers reaping
128*1000Sxc151355 	 * descriptors, never transmitting frames.  Aside from
129*1000Sxc151355 	 * reducing interrupts this also permits more concurrency.
130*1000Sxc151355 	 * The only potential downside is if the tx queue backs
131*1000Sxc151355 	 * up in which case the top half of the kernel may backup
132*1000Sxc151355 	 * due to a lack of tx descriptors.
133*1000Sxc151355 	 */
134*1000Sxc151355 	qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
135*1000Sxc151355 	qnum = ATH_HAL_SETUPTXQUEUE(ah, HAL_TX_QUEUE_DATA, &qi);
136*1000Sxc151355 	if (qnum == -1) {
137*1000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
138*1000Sxc151355 		    "Unable to setup hardware queue for %s traffic!\n",
139*1000Sxc151355 		    acnames[ac]));
140*1000Sxc151355 		return (1);
141*1000Sxc151355 	}
142*1000Sxc151355 	if (qnum >= ATH_N(asc->asc_txq)) {
143*1000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
144*1000Sxc151355 		    "hal qnum %u out of range, max %u!\n",
145*1000Sxc151355 		    qnum, ATH_N(asc->asc_txq)));
146*1000Sxc151355 		return (1);
147*1000Sxc151355 	}
148*1000Sxc151355 	if (!ATH_TXQ_SETUP(asc, qnum)) {
149*1000Sxc151355 		txq = &asc->asc_txq[qnum];
150*1000Sxc151355 		txq->axq_qnum = qnum;
151*1000Sxc151355 		txq->axq_depth = 0;
152*1000Sxc151355 		txq->axq_intrcnt = 0;
153*1000Sxc151355 		txq->axq_link = NULL;
154*1000Sxc151355 		list_create(&txq->axq_list, sizeof (struct ath_buf),
155*1000Sxc151355 		    offsetof(struct ath_buf, bf_node));
156*1000Sxc151355 		mutex_init(&txq->axq_lock, NULL, MUTEX_DRIVER, NULL);
157*1000Sxc151355 		asc->asc_txqsetup |= 1<<qnum;
158*1000Sxc151355 	}
159*1000Sxc151355 	asc->asc_ac2q[ac] = &asc->asc_txq[qnum];
160*1000Sxc151355 	return (0);
161*1000Sxc151355 }
162*1000Sxc151355 
163*1000Sxc151355 int
164*1000Sxc151355 ath_txq_setup(ath_t *asc)
165*1000Sxc151355 {
166*1000Sxc151355 	if (ath_set_data_queue(asc, WME_AC_BE, HAL_WME_AC_BK) ||
167*1000Sxc151355 	    ath_set_data_queue(asc, WME_AC_BK, HAL_WME_AC_BE) ||
168*1000Sxc151355 	    ath_set_data_queue(asc, WME_AC_VI, HAL_WME_AC_VI) ||
169*1000Sxc151355 	    ath_set_data_queue(asc, WME_AC_VO, HAL_WME_AC_VO)) {
170*1000Sxc151355 		return (1);
171*1000Sxc151355 	}
172*1000Sxc151355 
173*1000Sxc151355 	return (0);
174*1000Sxc151355 }
175*1000Sxc151355 
176*1000Sxc151355 void
177*1000Sxc151355 ath_setcurmode(ath_t *asc, enum ieee80211_phymode mode)
178*1000Sxc151355 {
179*1000Sxc151355 	const HAL_RATE_TABLE *rt;
180*1000Sxc151355 	int i;
181*1000Sxc151355 
182*1000Sxc151355 	for (i = 0; i < sizeof (asc->asc_rixmap); i++)
183*1000Sxc151355 		asc->asc_rixmap[i] = 0xff;
184*1000Sxc151355 
185*1000Sxc151355 	rt = asc->asc_rates[mode];
186*1000Sxc151355 	ASSERT(rt != NULL);
187*1000Sxc151355 
188*1000Sxc151355 	for (i = 0; i < rt->rateCount; i++)
189*1000Sxc151355 		asc->asc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
190*1000Sxc151355 
191*1000Sxc151355 	asc->asc_currates = rt;
192*1000Sxc151355 	asc->asc_curmode = mode;
193*1000Sxc151355 }
194*1000Sxc151355 
195*1000Sxc151355 /* Set correct parameters for a certain mode */
196*1000Sxc151355 void
197*1000Sxc151355 ath_mode_init(ath_t *asc)
198*1000Sxc151355 {
199*1000Sxc151355 	ieee80211com_t *isc = (ieee80211com_t *)asc;
200*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
201*1000Sxc151355 	uint32_t rfilt;
202*1000Sxc151355 
203*1000Sxc151355 	/* configure rx filter */
204*1000Sxc151355 	rfilt = ath_calcrxfilter(asc);
205*1000Sxc151355 	ATH_HAL_SETRXFILTER(ah, rfilt);
206*1000Sxc151355 	ATH_HAL_SETOPMODE(ah);
207*1000Sxc151355 	ATH_HAL_SETMCASTFILTER(ah, asc->asc_mfilt[0], asc->asc_mfilt[1]);
208*1000Sxc151355 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_mode_init(): "
209*1000Sxc151355 	    "mode =%d RX filter 0x%x, MC filter %08x:%08x\n",
210*1000Sxc151355 	    isc->isc_opmode, rfilt,
211*1000Sxc151355 	    asc->asc_mfilt[0], asc->asc_mfilt[1]));
212*1000Sxc151355 }
213*1000Sxc151355 
214*1000Sxc151355 
215*1000Sxc151355 /*
216*1000Sxc151355  * Disable the receive h/w in preparation for a reset.
217*1000Sxc151355  */
218*1000Sxc151355 void
219*1000Sxc151355 ath_stoprecv(ath_t *asc)
220*1000Sxc151355 {
221*1000Sxc151355 	ATH_HAL_STOPPCURECV(asc->asc_ah);	/* disable PCU */
222*1000Sxc151355 	ATH_HAL_SETRXFILTER(asc->asc_ah, 0);	/* clear recv filter */
223*1000Sxc151355 	ATH_HAL_STOPDMARECV(asc->asc_ah);	/* disable DMA engine */
224*1000Sxc151355 	drv_usecwait(3000);
225*1000Sxc151355 
226*1000Sxc151355 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_stoprecv(): rx queue %p, link %p\n",
227*1000Sxc151355 	    ATH_HAL_GETRXBUF(asc->asc_ah), asc->asc_rxlink));
228*1000Sxc151355 	asc->asc_rxlink = NULL;
229*1000Sxc151355 }
230*1000Sxc151355 
231*1000Sxc151355 uint32_t
232*1000Sxc151355 ath_chan2flags(ieee80211com_t *isc, struct ieee80211channel *chan)
233*1000Sxc151355 {
234*1000Sxc151355 	static const uint32_t modeflags[] = {
235*1000Sxc151355 	    0,				/* IEEE80211_MODE_AUTO */
236*1000Sxc151355 	    CHANNEL_A,			/* IEEE80211_MODE_11A */
237*1000Sxc151355 	    CHANNEL_B,			/* IEEE80211_MODE_11B */
238*1000Sxc151355 	    CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
239*1000Sxc151355 	    CHANNEL_T			/* IEEE80211_MODE_TURBO */
240*1000Sxc151355 	};
241*1000Sxc151355 	return (modeflags[ieee80211_chan2mode(isc, chan)]);
242*1000Sxc151355 }
243*1000Sxc151355 
244*1000Sxc151355 
245*1000Sxc151355 int
246*1000Sxc151355 ath_getchannels(ath_t *asc, uint32_t cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
247*1000Sxc151355 {
248*1000Sxc151355 	ieee80211com_t *isc = (ieee80211com_t *)asc;
249*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
250*1000Sxc151355 	HAL_CHANNEL *chans;
251*1000Sxc151355 	int i, ix;
252*1000Sxc151355 	uint32_t nchan;
253*1000Sxc151355 
254*1000Sxc151355 	chans = (HAL_CHANNEL *)
255*1000Sxc151355 	    kmem_zalloc(IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL), KM_SLEEP);
256*1000Sxc151355 
257*1000Sxc151355 	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
258*1000Sxc151355 	    cc, HAL_MODE_ALL, outdoor, xchanmode)) {
259*1000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
260*1000Sxc151355 		    "unable to get channel list\n");
261*1000Sxc151355 		kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL)));
262*1000Sxc151355 		return (EINVAL);
263*1000Sxc151355 	}
264*1000Sxc151355 
265*1000Sxc151355 	/*
266*1000Sxc151355 	 * Convert HAL channels to ieee80211 ones and insert
267*1000Sxc151355 	 * them in the table according to their channel number.
268*1000Sxc151355 	 */
269*1000Sxc151355 	for (i = 0; i < nchan; i++) {
270*1000Sxc151355 		HAL_CHANNEL *c = &chans[i];
271*1000Sxc151355 		ix = ath_hal_mhz2ieee(c->channel, c->channelFlags);
272*1000Sxc151355 		if (ix > IEEE80211_CHAN_MAX) {
273*1000Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
274*1000Sxc151355 			    "bad hal channel %u (%u/%x) ignored\n",
275*1000Sxc151355 			    ix, c->channel, c->channelFlags));
276*1000Sxc151355 			continue;
277*1000Sxc151355 		}
278*1000Sxc151355 		/* NB: flags are known to be compatible */
279*1000Sxc151355 		if (isc->isc_channels[ix].ich_freq == 0) {
280*1000Sxc151355 			isc->isc_channels[ix].ich_freq = c->channel;
281*1000Sxc151355 			isc->isc_channels[ix].ich_flags = c->channelFlags;
282*1000Sxc151355 		} else {
283*1000Sxc151355 			/* channels overlap; e.g. 11g and 11b */
284*1000Sxc151355 			isc->isc_channels[ix].ich_flags |= c->channelFlags;
285*1000Sxc151355 		}
286*1000Sxc151355 		if ((c->channelFlags & CHANNEL_G) == CHANNEL_G)
287*1000Sxc151355 			asc->asc_have11g = 1;
288*1000Sxc151355 	}
289*1000Sxc151355 	kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
290*1000Sxc151355 	return (0);
291*1000Sxc151355 }
292*1000Sxc151355 
293*1000Sxc151355 static void
294*1000Sxc151355 ath_drainq(ath_t *asc, struct ath_txq *txq)
295*1000Sxc151355 {
296*1000Sxc151355 	struct ath_buf *bf;
297*1000Sxc151355 
298*1000Sxc151355 	/*
299*1000Sxc151355 	 * This assumes output has been stopped.
300*1000Sxc151355 	 */
301*1000Sxc151355 	for (;;) {
302*1000Sxc151355 		mutex_enter(&txq->axq_lock);
303*1000Sxc151355 		bf = list_head(&txq->axq_list);
304*1000Sxc151355 		if (bf == NULL) {
305*1000Sxc151355 			txq->axq_link = NULL;
306*1000Sxc151355 			mutex_exit(&txq->axq_lock);
307*1000Sxc151355 			break;
308*1000Sxc151355 		}
309*1000Sxc151355 		list_remove(&txq->axq_list, bf);
310*1000Sxc151355 		mutex_exit(&txq->axq_lock);
311*1000Sxc151355 		bf->bf_in = NULL;
312*1000Sxc151355 		mutex_enter(&asc->asc_txbuflock);
313*1000Sxc151355 		list_insert_tail(&asc->asc_txbuf_list, bf);
314*1000Sxc151355 		mutex_exit(&asc->asc_txbuflock);
315*1000Sxc151355 	}
316*1000Sxc151355 }
317*1000Sxc151355 
318*1000Sxc151355 
319*1000Sxc151355 /*
320*1000Sxc151355  * Drain the transmit queues and reclaim resources.
321*1000Sxc151355  */
322*1000Sxc151355 void
323*1000Sxc151355 ath_draintxq(ath_t *asc)
324*1000Sxc151355 {
325*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
326*1000Sxc151355 	struct ath_txq *txq;
327*1000Sxc151355 	int i;
328*1000Sxc151355 
329*1000Sxc151355 	if (!asc->asc_invalid) {
330*1000Sxc151355 		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
331*1000Sxc151355 			if (ATH_TXQ_SETUP(asc, i)) {
332*1000Sxc151355 				txq = &asc->asc_txq[i];
333*1000Sxc151355 				(void) ATH_HAL_STOPTXDMA(ah, txq->axq_qnum);
334*1000Sxc151355 			}
335*1000Sxc151355 		}
336*1000Sxc151355 	}
337*1000Sxc151355 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
338*1000Sxc151355 		if (ATH_TXQ_SETUP(asc, i)) {
339*1000Sxc151355 			ath_drainq(asc, &asc->asc_txq[i]);
340*1000Sxc151355 		}
341*1000Sxc151355 	}
342*1000Sxc151355 }
343*1000Sxc151355 
344*1000Sxc151355 
345*1000Sxc151355 /* Enable the receive h/w following a reset */
346*1000Sxc151355 int
347*1000Sxc151355 ath_startrecv(ath_t *asc)
348*1000Sxc151355 {
349*1000Sxc151355 	struct ath_buf *bf;
350*1000Sxc151355 
351*1000Sxc151355 	asc->asc_rxlink = NULL;
352*1000Sxc151355 
353*1000Sxc151355 	bf = list_head(&asc->asc_rxbuf_list);
354*1000Sxc151355 	while (bf != NULL) {
355*1000Sxc151355 		ath_setup_desc(asc, bf);
356*1000Sxc151355 		bf = list_next(&asc->asc_rxbuf_list, bf);
357*1000Sxc151355 	}
358*1000Sxc151355 
359*1000Sxc151355 	bf = list_head(&asc->asc_rxbuf_list);
360*1000Sxc151355 	ATH_HAL_PUTRXBUF(asc->asc_ah, bf->bf_daddr);
361*1000Sxc151355 	ATH_HAL_RXENA(asc->asc_ah);		/* enable recv descriptors */
362*1000Sxc151355 	ath_mode_init(asc);			/* set filters, etc. */
363*1000Sxc151355 	ATH_HAL_STARTPCURECV(asc->asc_ah);	/* re-enable PCU/DMA engine */
364*1000Sxc151355 	return (0);
365*1000Sxc151355 }
366*1000Sxc151355 
367*1000Sxc151355 /*
368*1000Sxc151355  * Set/change channels.  If the channel is really being changed,
369*1000Sxc151355  * it's done by resetting the chip.  To accomplish this we must
370*1000Sxc151355  * first cleanup any pending DMA.
371*1000Sxc151355  */
372*1000Sxc151355 int
373*1000Sxc151355 ath_chan_set(ath_t *asc, struct ieee80211channel *chan)
374*1000Sxc151355 {
375*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
376*1000Sxc151355 	ieee80211com_t *isc = &asc->asc_isc;
377*1000Sxc151355 
378*1000Sxc151355 	if (chan != isc->isc_ibss_chan) {
379*1000Sxc151355 		HAL_STATUS status;
380*1000Sxc151355 		HAL_CHANNEL hchan;
381*1000Sxc151355 		enum ieee80211_phymode mode;
382*1000Sxc151355 
383*1000Sxc151355 		/*
384*1000Sxc151355 		 * To switch channels clear any pending DMA operations;
385*1000Sxc151355 		 * wait long enough for the RX fifo to drain, reset the
386*1000Sxc151355 		 * hardware at the new frequency, and then re-enable
387*1000Sxc151355 		 * the relevant bits of the h/w.
388*1000Sxc151355 		 */
389*1000Sxc151355 		ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
390*1000Sxc151355 		ath_draintxq(asc);		/* clear pending tx frames */
391*1000Sxc151355 		ath_stoprecv(asc);		/* turn off frame recv */
392*1000Sxc151355 		/*
393*1000Sxc151355 		 * Convert to a HAL channel description with
394*1000Sxc151355 		 * the flags constrained to reflect the current
395*1000Sxc151355 		 * operating mode.
396*1000Sxc151355 		 */
397*1000Sxc151355 		hchan.channel = chan->ich_freq;
398*1000Sxc151355 		hchan.channelFlags = ath_chan2flags(isc, chan);
399*1000Sxc151355 		if (!ATH_HAL_RESET(ah, (HAL_OPMODE)isc->isc_opmode,
400*1000Sxc151355 		    &hchan, AH_TRUE, &status)) {
401*1000Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_chan_set():"
402*1000Sxc151355 			    "unable to reset channel %u (%uMhz)\n",
403*1000Sxc151355 			    ieee80211_chan2ieee(isc, chan), chan->ich_freq));
404*1000Sxc151355 			return (EIO);
405*1000Sxc151355 		}
406*1000Sxc151355 
407*1000Sxc151355 		/*
408*1000Sxc151355 		 * Re-enable rx framework.
409*1000Sxc151355 		 */
410*1000Sxc151355 		if (ath_startrecv(asc) != 0) {
411*1000Sxc151355 			ath_problem("ath: ath_chan_set(): "
412*1000Sxc151355 			    "restarting receiving logic failed\n");
413*1000Sxc151355 			return (EIO);
414*1000Sxc151355 		}
415*1000Sxc151355 
416*1000Sxc151355 		/*
417*1000Sxc151355 		 * Change channels and update the h/w rate map
418*1000Sxc151355 		 * if we're switching; e.g. 11a to 11b/g.
419*1000Sxc151355 		 */
420*1000Sxc151355 		isc->isc_ibss_chan = chan;
421*1000Sxc151355 		mode = ieee80211_chan2mode(isc, chan);
422*1000Sxc151355 		if (mode != asc->asc_curmode)
423*1000Sxc151355 			ath_setcurmode(asc, mode);
424*1000Sxc151355 		/*
425*1000Sxc151355 		 * Re-enable interrupts.
426*1000Sxc151355 		 */
427*1000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
428*1000Sxc151355 	}
429*1000Sxc151355 	return (0);
430*1000Sxc151355 }
431*1000Sxc151355 
432*1000Sxc151355 
433*1000Sxc151355 /*
434*1000Sxc151355  * Configure the beacon and sleep timers.
435*1000Sxc151355  *
436*1000Sxc151355  * When operating as an AP this resets the TSF and sets
437*1000Sxc151355  * up the hardware to notify us when we need to issue beacons.
438*1000Sxc151355  *
439*1000Sxc151355  * When operating in station mode this sets up the beacon
440*1000Sxc151355  * timers according to the timestamp of the last received
441*1000Sxc151355  * beacon and the current TSF, configures PCF and DTIM
442*1000Sxc151355  * handling, programs the sleep registers so the hardware
443*1000Sxc151355  * will wakeup in time to receive beacons, and configures
444*1000Sxc151355  * the beacon miss handling so we'll receive a BMISS
445*1000Sxc151355  * interrupt when we stop seeing beacons from the AP
446*1000Sxc151355  * we've associated with.
447*1000Sxc151355  */
448*1000Sxc151355 void
449*1000Sxc151355 ath_beacon_config(ath_t *asc)
450*1000Sxc151355 {
451*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
452*1000Sxc151355 	ieee80211com_t *isc = (ieee80211com_t *)asc;
453*1000Sxc151355 	struct ieee80211_node *in = isc->isc_bss;
454*1000Sxc151355 	uint32_t nexttbtt;
455*1000Sxc151355 
456*1000Sxc151355 	nexttbtt = (ATH_LE_READ_4(in->in_tstamp + 4) << 22) |
457*1000Sxc151355 	    (ATH_LE_READ_4(in->in_tstamp) >> 10);
458*1000Sxc151355 	nexttbtt += in->in_intval;
459*1000Sxc151355 	if (isc->isc_opmode != IEEE80211_M_HOSTAP) {
460*1000Sxc151355 		HAL_BEACON_STATE bs;
461*1000Sxc151355 		uint32_t bmisstime;
462*1000Sxc151355 
463*1000Sxc151355 		/* NB: no PCF support right now */
464*1000Sxc151355 		bzero(&bs, sizeof (bs));
465*1000Sxc151355 		bs.bs_intval = in->in_intval;
466*1000Sxc151355 		bs.bs_nexttbtt = nexttbtt;
467*1000Sxc151355 		bs.bs_dtimperiod = bs.bs_intval;
468*1000Sxc151355 		bs.bs_nextdtim = nexttbtt;
469*1000Sxc151355 
470*1000Sxc151355 		/*
471*1000Sxc151355 		 * Calculate the number of consecutive beacons to miss
472*1000Sxc151355 		 * before taking a BMISS interrupt.  The configuration
473*1000Sxc151355 		 * is specified in ms, so we need to convert that to
474*1000Sxc151355 		 * TU's and then calculate based on the beacon interval.
475*1000Sxc151355 		 * Note that we clamp the result to at most 10 beacons.
476*1000Sxc151355 		 */
477*1000Sxc151355 		bmisstime = (isc->isc_bmisstimeout * 1000) / 1024;
478*1000Sxc151355 		bs.bs_bmissthreshold = howmany(bmisstime, in->in_intval);
479*1000Sxc151355 		if (bs.bs_bmissthreshold > 10)
480*1000Sxc151355 			bs.bs_bmissthreshold = 10;
481*1000Sxc151355 		else if (bs.bs_bmissthreshold <= 0)
482*1000Sxc151355 			bs.bs_bmissthreshold = 1;
483*1000Sxc151355 		/*
484*1000Sxc151355 		 * Calculate sleep duration.  The configuration is
485*1000Sxc151355 		 * given in ms.  We insure a multiple of the beacon
486*1000Sxc151355 		 * period is used.  Also, if the sleep duration is
487*1000Sxc151355 		 * greater than the DTIM period then it makes senses
488*1000Sxc151355 		 * to make it a multiple of that.
489*1000Sxc151355 		 */
490*1000Sxc151355 		bs.bs_sleepduration =
491*1000Sxc151355 		    roundup((100 * 1000) / 1024, bs.bs_intval);
492*1000Sxc151355 		if (bs.bs_sleepduration > bs.bs_dtimperiod)
493*1000Sxc151355 			bs.bs_sleepduration =
494*1000Sxc151355 			    roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
495*1000Sxc151355 
496*1000Sxc151355 
497*1000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_beacon_config(): "
498*1000Sxc151355 		    "intval %u nexttbtt %u dtim %u"
499*1000Sxc151355 		    " nextdtim %u bmiss %u sleep %u\n",
500*1000Sxc151355 		    bs.bs_intval,
501*1000Sxc151355 		    bs.bs_nexttbtt,
502*1000Sxc151355 		    bs.bs_dtimperiod,
503*1000Sxc151355 		    bs.bs_nextdtim,
504*1000Sxc151355 		    bs.bs_bmissthreshold,
505*1000Sxc151355 		    bs.bs_sleepduration));
506*1000Sxc151355 		ATH_HAL_INTRSET(ah, 0);
507*1000Sxc151355 		/*
508*1000Sxc151355 		 * Reset our tsf so the hardware will update the
509*1000Sxc151355 		 * tsf register to reflect timestamps found in
510*1000Sxc151355 		 * received beacons.
511*1000Sxc151355 		 */
512*1000Sxc151355 		ATH_HAL_RESETTSF(ah);
513*1000Sxc151355 		ATH_HAL_BEACONTIMERS(ah, &bs);
514*1000Sxc151355 		asc->asc_imask |= HAL_INT_BMISS;
515*1000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
516*1000Sxc151355 	} else {
517*1000Sxc151355 		ATH_HAL_INTRSET(ah, 0);
518*1000Sxc151355 		ATH_HAL_BEACONINIT(ah, nexttbtt, in->in_intval);
519*1000Sxc151355 		asc->asc_imask |= HAL_INT_SWBA;	/* beacon prepare */
520*1000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
521*1000Sxc151355 	}
522*1000Sxc151355 }
523*1000Sxc151355 
524*1000Sxc151355 
525*1000Sxc151355 
526*1000Sxc151355 /*
527*1000Sxc151355  * Fill the hardware key cache with key entries.
528*1000Sxc151355  */
529*1000Sxc151355 void
530*1000Sxc151355 ath_initkeytable(ath_t *asc)
531*1000Sxc151355 {
532*1000Sxc151355 	ieee80211com_t *isc = (ieee80211com_t *)asc;
533*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
534*1000Sxc151355 	int32_t i;
535*1000Sxc151355 
536*1000Sxc151355 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
537*1000Sxc151355 		struct ieee80211_wepkey *k = &isc->isc_nw_keys[i];
538*1000Sxc151355 		if (k->iwk_len == 0)
539*1000Sxc151355 			ATH_HAL_KEYRESET(ah, i);
540*1000Sxc151355 		else {
541*1000Sxc151355 			HAL_KEYVAL hk;
542*1000Sxc151355 
543*1000Sxc151355 #ifdef DEBUG
544*1000Sxc151355 			char tmp[200], stmp[10];
545*1000Sxc151355 			int j;
546*1000Sxc151355 			bzero(tmp, 200);
547*1000Sxc151355 			bzero(stmp, 10);
548*1000Sxc151355 			for (j = 0; j < k->iwk_len; j++) {
549*1000Sxc151355 				(void) sprintf(stmp, "0x%02x ", k->iwk_key[j]);
550*1000Sxc151355 				(void) strcat(tmp, stmp);
551*1000Sxc151355 			}
552*1000Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_initkeytable(): "
553*1000Sxc151355 			    "key%d val=%s\n", i, tmp));
554*1000Sxc151355 #endif /* DEBUG */
555*1000Sxc151355 			bzero(&hk, sizeof (hk));
556*1000Sxc151355 			hk.kv_type = HAL_CIPHER_WEP;
557*1000Sxc151355 			hk.kv_len = k->iwk_len;
558*1000Sxc151355 			bcopy(k->iwk_key, hk.kv_val, k->iwk_len);
559*1000Sxc151355 			ATH_HAL_KEYSET(ah, i, &hk);
560*1000Sxc151355 		}
561*1000Sxc151355 	}
562*1000Sxc151355 }
563*1000Sxc151355 
564*1000Sxc151355 void
565*1000Sxc151355 ath_reset(ath_t *asc)
566*1000Sxc151355 {
567*1000Sxc151355 	ieee80211com_t *isc = (ieee80211com_t *)asc;
568*1000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
569*1000Sxc151355 	struct ieee80211channel *ch;
570*1000Sxc151355 	HAL_STATUS status;
571*1000Sxc151355 	HAL_CHANNEL hchan;
572*1000Sxc151355 
573*1000Sxc151355 	/*
574*1000Sxc151355 	 * Convert to a HAL channel description with the flags
575*1000Sxc151355 	 * constrained to reflect the current operating mode.
576*1000Sxc151355 	 */
577*1000Sxc151355 	ch = isc->isc_ibss_chan;
578*1000Sxc151355 	hchan.channel = ch->ich_freq;
579*1000Sxc151355 	hchan.channelFlags = ath_chan2flags(isc, ch);
580*1000Sxc151355 
581*1000Sxc151355 	ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
582*1000Sxc151355 	ath_draintxq(asc);		/* stop xmit side */
583*1000Sxc151355 	if (asc->asc_invalid == 0)
584*1000Sxc151355 		ath_stoprecv(asc);		/* stop recv side */
585*1000Sxc151355 	/* indicate channel change so we do a full reset */
586*1000Sxc151355 	if (!ATH_HAL_RESET(ah, (HAL_OPMODE)isc->isc_opmode, &hchan,
587*1000Sxc151355 	    AH_TRUE, &status)) {
588*1000Sxc151355 		ath_problem("ath: ath_reset(): "
589*1000Sxc151355 		    "reseting hardware failed, HAL status %u\n", status);
590*1000Sxc151355 	}
591*1000Sxc151355 	if (asc->asc_invalid == 0) {
592*1000Sxc151355 		ath_initkeytable(asc);
593*1000Sxc151355 		if (ath_startrecv(asc) != 0)	/* restart recv */
594*1000Sxc151355 			ath_problem("ath: ath_reset(): "
595*1000Sxc151355 			    "starting receiving logic failed\n");
596*1000Sxc151355 		if (isc->isc_state == IEEE80211_S_RUN) {
597*1000Sxc151355 			ath_beacon_config(asc);	/* restart beacons */
598*1000Sxc151355 		}
599*1000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
600*1000Sxc151355 	}
601*1000Sxc151355 }
602