xref: /onnv-gate/usr/src/uts/common/io/ath/ath_aux.c (revision 3147:2789cc0027be)
11000Sxc151355 /*
2*3147Sxc151355  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
31000Sxc151355  * Use is subject to license terms.
41000Sxc151355  */
51000Sxc151355 
61000Sxc151355 /*
71000Sxc151355  * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
81000Sxc151355  * All rights reserved.
91000Sxc151355  *
101000Sxc151355  * Redistribution and use in source and binary forms, with or without
111000Sxc151355  * modification, are permitted provided that the following conditions
121000Sxc151355  * are met:
131000Sxc151355  * 1. Redistributions of source code must retain the above copyright
141000Sxc151355  * notice, this list of conditions and the following disclaimer,
151000Sxc151355  * without modification.
161000Sxc151355  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
171000Sxc151355  * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
181000Sxc151355  * redistribution must be conditioned upon including a substantially
191000Sxc151355  * similar Disclaimer requirement for further binary redistribution.
201000Sxc151355  * 3. Neither the names of the above-listed copyright holders nor the names
211000Sxc151355  * of any contributors may be used to endorse or promote products derived
221000Sxc151355  * from this software without specific prior written permission.
231000Sxc151355  *
241000Sxc151355  * NO WARRANTY
251000Sxc151355  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
261000Sxc151355  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
271000Sxc151355  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
281000Sxc151355  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
291000Sxc151355  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
301000Sxc151355  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
311000Sxc151355  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
321000Sxc151355  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
331000Sxc151355  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
341000Sxc151355  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
351000Sxc151355  * THE POSSIBILITY OF SUCH DAMAGES.
361000Sxc151355  */
371000Sxc151355 
381000Sxc151355 #pragma ident	"%Z%%M%	%I%	%E% SMI"
391000Sxc151355 
401000Sxc151355 #include <sys/param.h>
411000Sxc151355 #include <sys/types.h>
421000Sxc151355 #include <sys/signal.h>
431000Sxc151355 #include <sys/stream.h>
441000Sxc151355 #include <sys/termio.h>
451000Sxc151355 #include <sys/errno.h>
461000Sxc151355 #include <sys/file.h>
471000Sxc151355 #include <sys/cmn_err.h>
481000Sxc151355 #include <sys/stropts.h>
491000Sxc151355 #include <sys/strsubr.h>
501000Sxc151355 #include <sys/strtty.h>
511000Sxc151355 #include <sys/kbio.h>
521000Sxc151355 #include <sys/cred.h>
531000Sxc151355 #include <sys/stat.h>
541000Sxc151355 #include <sys/consdev.h>
551000Sxc151355 #include <sys/kmem.h>
561000Sxc151355 #include <sys/modctl.h>
571000Sxc151355 #include <sys/ddi.h>
581000Sxc151355 #include <sys/sunddi.h>
591000Sxc151355 #include <sys/pci.h>
601000Sxc151355 #include <sys/errno.h>
611000Sxc151355 #include <sys/gld.h>
621000Sxc151355 #include <sys/dlpi.h>
631000Sxc151355 #include <sys/ethernet.h>
641000Sxc151355 #include <sys/list.h>
651000Sxc151355 #include <sys/byteorder.h>
661000Sxc151355 #include <sys/strsun.h>
671000Sxc151355 #include <inet/common.h>
681000Sxc151355 #include <inet/nd.h>
691000Sxc151355 #include <inet/mi.h>
701000Sxc151355 #include <inet/wifi_ioctl.h>
711000Sxc151355 #include "ath_hal.h"
721000Sxc151355 #include "ath_impl.h"
731000Sxc151355 
741000Sxc151355 static const char *acnames[] = {
751000Sxc151355 	"WME_AC_BE",
761000Sxc151355 	"WME_AC_BK",
771000Sxc151355 	"WME_AC_VI",
781000Sxc151355 	"WME_AC_VO",
791000Sxc151355 	"WME_UPSD"
801000Sxc151355 };
811000Sxc151355 
821000Sxc151355 extern void ath_setup_desc(ath_t *asc, struct ath_buf *bf);
831000Sxc151355 
841000Sxc151355 uint32_t
851000Sxc151355 ath_calcrxfilter(ath_t *asc)
861000Sxc151355 {
87*3147Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
881000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
891000Sxc151355 	uint32_t rfilt;
901000Sxc151355 
911000Sxc151355 	rfilt = (ATH_HAL_GETRXFILTER(ah) & HAL_RX_FILTER_PHYERR)
921000Sxc151355 	    | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
93*3147Sxc151355 	if (ic->ic_opmode != IEEE80211_M_STA)
941000Sxc151355 		rfilt |= HAL_RX_FILTER_PROBEREQ;
95*3147Sxc151355 	if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
961000Sxc151355 	    (asc->asc_promisc & GLD_MAC_PROMISC_PHYS))	/* promiscuous */
971000Sxc151355 		rfilt |= HAL_RX_FILTER_PROM;
98*3147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA ||
99*3147Sxc151355 	    ic->ic_opmode == IEEE80211_M_IBSS ||
100*3147Sxc151355 	    ic->ic_state == IEEE80211_S_SCAN)
1011000Sxc151355 		rfilt |= HAL_RX_FILTER_BEACON;
1021000Sxc151355 	return (rfilt);
1031000Sxc151355 }
1041000Sxc151355 
1051000Sxc151355 static int
1061000Sxc151355 ath_set_data_queue(ath_t *asc, int ac, int haltype)
1071000Sxc151355 {
1081000Sxc151355 	HAL_TXQ_INFO qi;
1091000Sxc151355 	int qnum;
1101000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
1111000Sxc151355 	struct ath_txq *txq;
1121000Sxc151355 
1131000Sxc151355 	if (ac >= ATH_N(asc->asc_ac2q)) {
1141000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
1151000Sxc151355 		    "ac %u out of range, max %u!\n",
1161000Sxc151355 		    ac, ATH_N(asc->asc_ac2q)));
1171000Sxc151355 		return (1);
1181000Sxc151355 	}
1191000Sxc151355 	(void) memset(&qi, 0, sizeof (qi));
1201000Sxc151355 	qi.tqi_subtype = haltype;
1211000Sxc151355 	/*
1221000Sxc151355 	 * Enable interrupts only for EOL and DESC conditions.
1231000Sxc151355 	 * We mark tx descriptors to receive a DESC interrupt
1241000Sxc151355 	 * when a tx queue gets deep; otherwise waiting for the
1251000Sxc151355 	 * EOL to reap descriptors.  Note that this is done to
1261000Sxc151355 	 * reduce interrupt load and this only defers reaping
1271000Sxc151355 	 * descriptors, never transmitting frames.  Aside from
1281000Sxc151355 	 * reducing interrupts this also permits more concurrency.
1291000Sxc151355 	 * The only potential downside is if the tx queue backs
1301000Sxc151355 	 * up in which case the top half of the kernel may backup
1311000Sxc151355 	 * due to a lack of tx descriptors.
1321000Sxc151355 	 */
133*3147Sxc151355 	qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE;
1341000Sxc151355 	qnum = ATH_HAL_SETUPTXQUEUE(ah, HAL_TX_QUEUE_DATA, &qi);
1351000Sxc151355 	if (qnum == -1) {
1361000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
1371000Sxc151355 		    "Unable to setup hardware queue for %s traffic!\n",
1381000Sxc151355 		    acnames[ac]));
1391000Sxc151355 		return (1);
1401000Sxc151355 	}
1411000Sxc151355 	if (qnum >= ATH_N(asc->asc_txq)) {
1421000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
1431000Sxc151355 		    "hal qnum %u out of range, max %u!\n",
1441000Sxc151355 		    qnum, ATH_N(asc->asc_txq)));
1451000Sxc151355 		return (1);
1461000Sxc151355 	}
1471000Sxc151355 	if (!ATH_TXQ_SETUP(asc, qnum)) {
1481000Sxc151355 		txq = &asc->asc_txq[qnum];
1491000Sxc151355 		txq->axq_qnum = qnum;
1501000Sxc151355 		txq->axq_depth = 0;
1511000Sxc151355 		txq->axq_intrcnt = 0;
1521000Sxc151355 		txq->axq_link = NULL;
1531000Sxc151355 		list_create(&txq->axq_list, sizeof (struct ath_buf),
1541000Sxc151355 		    offsetof(struct ath_buf, bf_node));
1551000Sxc151355 		mutex_init(&txq->axq_lock, NULL, MUTEX_DRIVER, NULL);
1561000Sxc151355 		asc->asc_txqsetup |= 1<<qnum;
1571000Sxc151355 	}
1581000Sxc151355 	asc->asc_ac2q[ac] = &asc->asc_txq[qnum];
1591000Sxc151355 	return (0);
1601000Sxc151355 }
1611000Sxc151355 
1621000Sxc151355 int
1631000Sxc151355 ath_txq_setup(ath_t *asc)
1641000Sxc151355 {
1651000Sxc151355 	if (ath_set_data_queue(asc, WME_AC_BE, HAL_WME_AC_BK) ||
1661000Sxc151355 	    ath_set_data_queue(asc, WME_AC_BK, HAL_WME_AC_BE) ||
1671000Sxc151355 	    ath_set_data_queue(asc, WME_AC_VI, HAL_WME_AC_VI) ||
1681000Sxc151355 	    ath_set_data_queue(asc, WME_AC_VO, HAL_WME_AC_VO)) {
1691000Sxc151355 		return (1);
1701000Sxc151355 	}
1711000Sxc151355 
1721000Sxc151355 	return (0);
1731000Sxc151355 }
1741000Sxc151355 
1751000Sxc151355 void
176*3147Sxc151355 ath_txq_cleanup(ath_t *asc)
177*3147Sxc151355 {
178*3147Sxc151355 	int i;
179*3147Sxc151355 
180*3147Sxc151355 	mutex_destroy(&asc->asc_txbuflock);
181*3147Sxc151355 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
182*3147Sxc151355 		if (ATH_TXQ_SETUP(asc, i)) {
183*3147Sxc151355 			struct ath_txq *txq = &asc->asc_txq[i];
184*3147Sxc151355 
185*3147Sxc151355 			ATH_HAL_RELEASETXQUEUE(asc->asc_ah, txq->axq_qnum);
186*3147Sxc151355 			mutex_destroy(&txq->axq_lock);
187*3147Sxc151355 			asc->asc_txqsetup &= ~(1 << txq->axq_qnum);
188*3147Sxc151355 		}
189*3147Sxc151355 	}
190*3147Sxc151355 }
191*3147Sxc151355 
192*3147Sxc151355 void
1931000Sxc151355 ath_setcurmode(ath_t *asc, enum ieee80211_phymode mode)
1941000Sxc151355 {
1951000Sxc151355 	const HAL_RATE_TABLE *rt;
1961000Sxc151355 	int i;
1971000Sxc151355 
1981000Sxc151355 	for (i = 0; i < sizeof (asc->asc_rixmap); i++)
1991000Sxc151355 		asc->asc_rixmap[i] = 0xff;
2001000Sxc151355 
2011000Sxc151355 	rt = asc->asc_rates[mode];
2021000Sxc151355 	ASSERT(rt != NULL);
2031000Sxc151355 
2041000Sxc151355 	for (i = 0; i < rt->rateCount; i++)
2051000Sxc151355 		asc->asc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
2061000Sxc151355 
2071000Sxc151355 	asc->asc_currates = rt;
2081000Sxc151355 	asc->asc_curmode = mode;
2091000Sxc151355 }
2101000Sxc151355 
2111000Sxc151355 /* Set correct parameters for a certain mode */
2121000Sxc151355 void
2131000Sxc151355 ath_mode_init(ath_t *asc)
2141000Sxc151355 {
215*3147Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
2161000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
2171000Sxc151355 	uint32_t rfilt;
2181000Sxc151355 
2191000Sxc151355 	/* configure rx filter */
2201000Sxc151355 	rfilt = ath_calcrxfilter(asc);
2211000Sxc151355 	ATH_HAL_SETRXFILTER(ah, rfilt);
2221000Sxc151355 	ATH_HAL_SETOPMODE(ah);
2231000Sxc151355 	ATH_HAL_SETMCASTFILTER(ah, asc->asc_mfilt[0], asc->asc_mfilt[1]);
2241000Sxc151355 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_mode_init(): "
2251000Sxc151355 	    "mode =%d RX filter 0x%x, MC filter %08x:%08x\n",
226*3147Sxc151355 	    ic->ic_opmode, rfilt,
2271000Sxc151355 	    asc->asc_mfilt[0], asc->asc_mfilt[1]));
2281000Sxc151355 }
2291000Sxc151355 
2301000Sxc151355 
2311000Sxc151355 /*
2321000Sxc151355  * Disable the receive h/w in preparation for a reset.
2331000Sxc151355  */
2341000Sxc151355 void
2351000Sxc151355 ath_stoprecv(ath_t *asc)
2361000Sxc151355 {
2371000Sxc151355 	ATH_HAL_STOPPCURECV(asc->asc_ah);	/* disable PCU */
2381000Sxc151355 	ATH_HAL_SETRXFILTER(asc->asc_ah, 0);	/* clear recv filter */
2391000Sxc151355 	ATH_HAL_STOPDMARECV(asc->asc_ah);	/* disable DMA engine */
2401000Sxc151355 	drv_usecwait(3000);
2411000Sxc151355 
2421000Sxc151355 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_stoprecv(): rx queue %p, link %p\n",
2431000Sxc151355 	    ATH_HAL_GETRXBUF(asc->asc_ah), asc->asc_rxlink));
2441000Sxc151355 	asc->asc_rxlink = NULL;
2451000Sxc151355 }
2461000Sxc151355 
2471000Sxc151355 uint32_t
248*3147Sxc151355 ath_chan2flags(ieee80211com_t *isc, struct ieee80211_channel *chan)
2491000Sxc151355 {
2501000Sxc151355 	static const uint32_t modeflags[] = {
2511000Sxc151355 	    0,				/* IEEE80211_MODE_AUTO */
2521000Sxc151355 	    CHANNEL_A,			/* IEEE80211_MODE_11A */
2531000Sxc151355 	    CHANNEL_B,			/* IEEE80211_MODE_11B */
2541000Sxc151355 	    CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
255*3147Sxc151355 	    0,				/* IEEE80211_MODE_FH */
256*3147Sxc151355 	    CHANNEL_108A,		/* IEEE80211_MODE_TURBO_A */
257*3147Sxc151355 	    CHANNEL_108G		/* IEEE80211_MODE_TURBO_G */
2581000Sxc151355 	};
2591000Sxc151355 	return (modeflags[ieee80211_chan2mode(isc, chan)]);
2601000Sxc151355 }
2611000Sxc151355 
2621000Sxc151355 
2631000Sxc151355 int
2641000Sxc151355 ath_getchannels(ath_t *asc, uint32_t cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
2651000Sxc151355 {
266*3147Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
2671000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
2681000Sxc151355 	HAL_CHANNEL *chans;
2691000Sxc151355 	int i, ix;
2701000Sxc151355 	uint32_t nchan;
2711000Sxc151355 
2721000Sxc151355 	chans = (HAL_CHANNEL *)
2731000Sxc151355 	    kmem_zalloc(IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL), KM_SLEEP);
2741000Sxc151355 
2751000Sxc151355 	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
276*3147Sxc151355 	    NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) {
2771000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
2781000Sxc151355 		    "unable to get channel list\n");
2791000Sxc151355 		kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL)));
2801000Sxc151355 		return (EINVAL);
2811000Sxc151355 	}
2821000Sxc151355 
2831000Sxc151355 	/*
2841000Sxc151355 	 * Convert HAL channels to ieee80211 ones and insert
2851000Sxc151355 	 * them in the table according to their channel number.
2861000Sxc151355 	 */
2871000Sxc151355 	for (i = 0; i < nchan; i++) {
2881000Sxc151355 		HAL_CHANNEL *c = &chans[i];
289*3147Sxc151355 		uint16_t flags;
290*3147Sxc151355 		ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags);
2911000Sxc151355 		if (ix > IEEE80211_CHAN_MAX) {
2921000Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
293*3147Sxc151355 			    "bad hal channel %d (%u/%x) ignored\n",
2941000Sxc151355 			    ix, c->channel, c->channelFlags));
2951000Sxc151355 			continue;
2961000Sxc151355 		}
2971000Sxc151355 		/* NB: flags are known to be compatible */
298*3147Sxc151355 		if (ix < 0) {
299*3147Sxc151355 			/*
300*3147Sxc151355 			 * can't handle frequency <2400MHz (negative
301*3147Sxc151355 			 * channels) right now
302*3147Sxc151355 			 */
303*3147Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath:ath_getchannels(): "
304*3147Sxc151355 			    "hal channel %d (%u/%x) "
305*3147Sxc151355 			    "cannot be handled, ignored\n",
306*3147Sxc151355 			    ix, c->channel, c->channelFlags));
307*3147Sxc151355 			continue;
308*3147Sxc151355 		}
309*3147Sxc151355 		/*
310*3147Sxc151355 		 * Calculate net80211 flags; most are compatible
311*3147Sxc151355 		 * but some need massaging.  Note the static turbo
312*3147Sxc151355 		 * conversion can be removed once net80211 is updated
313*3147Sxc151355 		 * to understand static vs. dynamic turbo.
314*3147Sxc151355 		 */
315*3147Sxc151355 		flags = c->channelFlags & CHANNEL_COMPAT;
316*3147Sxc151355 		if (c->channelFlags & CHANNEL_STURBO)
317*3147Sxc151355 			flags |= IEEE80211_CHAN_TURBO;
318*3147Sxc151355 		if (ic->ic_sup_channels[ix].ich_freq == 0) {
319*3147Sxc151355 			ic->ic_sup_channels[ix].ich_freq = c->channel;
320*3147Sxc151355 			ic->ic_sup_channels[ix].ich_flags = flags;
3211000Sxc151355 		} else {
3221000Sxc151355 			/* channels overlap; e.g. 11g and 11b */
323*3147Sxc151355 			ic->ic_sup_channels[ix].ich_flags |= flags;
3241000Sxc151355 		}
3251000Sxc151355 		if ((c->channelFlags & CHANNEL_G) == CHANNEL_G)
3261000Sxc151355 			asc->asc_have11g = 1;
3271000Sxc151355 	}
3281000Sxc151355 	kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
3291000Sxc151355 	return (0);
3301000Sxc151355 }
3311000Sxc151355 
3321000Sxc151355 static void
3331000Sxc151355 ath_drainq(ath_t *asc, struct ath_txq *txq)
3341000Sxc151355 {
3351000Sxc151355 	struct ath_buf *bf;
3361000Sxc151355 
3371000Sxc151355 	/*
3381000Sxc151355 	 * This assumes output has been stopped.
3391000Sxc151355 	 */
3401000Sxc151355 	for (;;) {
3411000Sxc151355 		mutex_enter(&txq->axq_lock);
3421000Sxc151355 		bf = list_head(&txq->axq_list);
3431000Sxc151355 		if (bf == NULL) {
3441000Sxc151355 			txq->axq_link = NULL;
3451000Sxc151355 			mutex_exit(&txq->axq_lock);
3461000Sxc151355 			break;
3471000Sxc151355 		}
3481000Sxc151355 		list_remove(&txq->axq_list, bf);
3491000Sxc151355 		mutex_exit(&txq->axq_lock);
3501000Sxc151355 		bf->bf_in = NULL;
3511000Sxc151355 		mutex_enter(&asc->asc_txbuflock);
3521000Sxc151355 		list_insert_tail(&asc->asc_txbuf_list, bf);
3531000Sxc151355 		mutex_exit(&asc->asc_txbuflock);
3541000Sxc151355 	}
3551000Sxc151355 }
3561000Sxc151355 
3571000Sxc151355 
3581000Sxc151355 /*
3591000Sxc151355  * Drain the transmit queues and reclaim resources.
3601000Sxc151355  */
3611000Sxc151355 void
3621000Sxc151355 ath_draintxq(ath_t *asc)
3631000Sxc151355 {
3641000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
3651000Sxc151355 	struct ath_txq *txq;
3661000Sxc151355 	int i;
3671000Sxc151355 
3681000Sxc151355 	if (!asc->asc_invalid) {
3691000Sxc151355 		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
3701000Sxc151355 			if (ATH_TXQ_SETUP(asc, i)) {
3711000Sxc151355 				txq = &asc->asc_txq[i];
3721000Sxc151355 				(void) ATH_HAL_STOPTXDMA(ah, txq->axq_qnum);
3731000Sxc151355 			}
3741000Sxc151355 		}
3751000Sxc151355 	}
3761000Sxc151355 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
3771000Sxc151355 		if (ATH_TXQ_SETUP(asc, i)) {
3781000Sxc151355 			ath_drainq(asc, &asc->asc_txq[i]);
3791000Sxc151355 		}
3801000Sxc151355 	}
3811000Sxc151355 }
3821000Sxc151355 
3831000Sxc151355 
3841000Sxc151355 /* Enable the receive h/w following a reset */
3851000Sxc151355 int
3861000Sxc151355 ath_startrecv(ath_t *asc)
3871000Sxc151355 {
3881000Sxc151355 	struct ath_buf *bf;
3891000Sxc151355 
3901000Sxc151355 	asc->asc_rxlink = NULL;
3911000Sxc151355 
3921000Sxc151355 	bf = list_head(&asc->asc_rxbuf_list);
3931000Sxc151355 	while (bf != NULL) {
3941000Sxc151355 		ath_setup_desc(asc, bf);
3951000Sxc151355 		bf = list_next(&asc->asc_rxbuf_list, bf);
3961000Sxc151355 	}
3971000Sxc151355 
3981000Sxc151355 	bf = list_head(&asc->asc_rxbuf_list);
3991000Sxc151355 	ATH_HAL_PUTRXBUF(asc->asc_ah, bf->bf_daddr);
4001000Sxc151355 	ATH_HAL_RXENA(asc->asc_ah);		/* enable recv descriptors */
4011000Sxc151355 	ath_mode_init(asc);			/* set filters, etc. */
4021000Sxc151355 	ATH_HAL_STARTPCURECV(asc->asc_ah);	/* re-enable PCU/DMA engine */
4031000Sxc151355 	return (0);
4041000Sxc151355 }
4051000Sxc151355 
4061000Sxc151355 /*
407*3147Sxc151355  * Update internal state after a channel change.
408*3147Sxc151355  */
409*3147Sxc151355 void
410*3147Sxc151355 ath_chan_change(ath_t *asc, struct ieee80211_channel *chan)
411*3147Sxc151355 {
412*3147Sxc151355 	struct ieee80211com *ic = &asc->asc_isc;
413*3147Sxc151355 	enum ieee80211_phymode mode;
414*3147Sxc151355 
415*3147Sxc151355 	/*
416*3147Sxc151355 	 * Change channels and update the h/w rate map
417*3147Sxc151355 	 * if we're switching; e.g. 11a to 11b/g.
418*3147Sxc151355 	 */
419*3147Sxc151355 	mode = ieee80211_chan2mode(ic, chan);
420*3147Sxc151355 	if (mode != asc->asc_curmode)
421*3147Sxc151355 		ath_setcurmode(asc, mode);
422*3147Sxc151355 }
423*3147Sxc151355 
424*3147Sxc151355 /*
4251000Sxc151355  * Set/change channels.  If the channel is really being changed,
4261000Sxc151355  * it's done by resetting the chip.  To accomplish this we must
4271000Sxc151355  * first cleanup any pending DMA.
4281000Sxc151355  */
4291000Sxc151355 int
430*3147Sxc151355 ath_chan_set(ath_t *asc, struct ieee80211_channel *chan)
4311000Sxc151355 {
4321000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
433*3147Sxc151355 	ieee80211com_t *ic = &asc->asc_isc;
4341000Sxc151355 
435*3147Sxc151355 	if (chan != ic->ic_ibss_chan) {
4361000Sxc151355 		HAL_STATUS status;
4371000Sxc151355 		HAL_CHANNEL hchan;
4381000Sxc151355 
4391000Sxc151355 		/*
4401000Sxc151355 		 * To switch channels clear any pending DMA operations;
4411000Sxc151355 		 * wait long enough for the RX fifo to drain, reset the
4421000Sxc151355 		 * hardware at the new frequency, and then re-enable
4431000Sxc151355 		 * the relevant bits of the h/w.
4441000Sxc151355 		 */
4451000Sxc151355 		ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
4461000Sxc151355 		ath_draintxq(asc);		/* clear pending tx frames */
4471000Sxc151355 		ath_stoprecv(asc);		/* turn off frame recv */
4481000Sxc151355 		/*
4491000Sxc151355 		 * Convert to a HAL channel description with
4501000Sxc151355 		 * the flags constrained to reflect the current
4511000Sxc151355 		 * operating mode.
4521000Sxc151355 		 */
4531000Sxc151355 		hchan.channel = chan->ich_freq;
454*3147Sxc151355 		hchan.channelFlags = ath_chan2flags(ic, chan);
455*3147Sxc151355 		if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
4561000Sxc151355 		    &hchan, AH_TRUE, &status)) {
4571000Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_chan_set():"
4581000Sxc151355 			    "unable to reset channel %u (%uMhz)\n",
459*3147Sxc151355 			    ieee80211_chan2ieee(ic, chan), chan->ich_freq));
4601000Sxc151355 			return (EIO);
4611000Sxc151355 		}
462*3147Sxc151355 		asc->asc_curchan = hchan;
4631000Sxc151355 
4641000Sxc151355 		/*
4651000Sxc151355 		 * Re-enable rx framework.
4661000Sxc151355 		 */
4671000Sxc151355 		if (ath_startrecv(asc) != 0) {
4681000Sxc151355 			ath_problem("ath: ath_chan_set(): "
4691000Sxc151355 			    "restarting receiving logic failed\n");
4701000Sxc151355 			return (EIO);
4711000Sxc151355 		}
4721000Sxc151355 
4731000Sxc151355 		/*
4741000Sxc151355 		 * Change channels and update the h/w rate map
4751000Sxc151355 		 * if we're switching; e.g. 11a to 11b/g.
4761000Sxc151355 		 */
477*3147Sxc151355 		ic->ic_ibss_chan = chan;
478*3147Sxc151355 		ath_chan_change(asc, chan);
4791000Sxc151355 		/*
4801000Sxc151355 		 * Re-enable interrupts.
4811000Sxc151355 		 */
4821000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
4831000Sxc151355 	}
4841000Sxc151355 	return (0);
4851000Sxc151355 }
4861000Sxc151355 
4871000Sxc151355 
4881000Sxc151355 /*
4891000Sxc151355  * Configure the beacon and sleep timers.
4901000Sxc151355  *
4911000Sxc151355  * When operating as an AP this resets the TSF and sets
4921000Sxc151355  * up the hardware to notify us when we need to issue beacons.
4931000Sxc151355  *
4941000Sxc151355  * When operating in station mode this sets up the beacon
4951000Sxc151355  * timers according to the timestamp of the last received
4961000Sxc151355  * beacon and the current TSF, configures PCF and DTIM
4971000Sxc151355  * handling, programs the sleep registers so the hardware
4981000Sxc151355  * will wakeup in time to receive beacons, and configures
4991000Sxc151355  * the beacon miss handling so we'll receive a BMISS
5001000Sxc151355  * interrupt when we stop seeing beacons from the AP
5011000Sxc151355  * we've associated with.
5021000Sxc151355  */
5031000Sxc151355 void
5041000Sxc151355 ath_beacon_config(ath_t *asc)
5051000Sxc151355 {
5061000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
507*3147Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
508*3147Sxc151355 	struct ieee80211_node *in = ic->ic_bss;
5091000Sxc151355 	uint32_t nexttbtt;
5101000Sxc151355 
511*3147Sxc151355 	nexttbtt = (ATH_LE_READ_4(in->in_tstamp.data + 4) << 22) |
512*3147Sxc151355 	    (ATH_LE_READ_4(in->in_tstamp.data) >> 10);
5131000Sxc151355 	nexttbtt += in->in_intval;
514*3147Sxc151355 	if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
5151000Sxc151355 		HAL_BEACON_STATE bs;
5161000Sxc151355 
5171000Sxc151355 		/* NB: no PCF support right now */
5181000Sxc151355 		bzero(&bs, sizeof (bs));
5191000Sxc151355 		bs.bs_intval = in->in_intval;
5201000Sxc151355 		bs.bs_nexttbtt = nexttbtt;
5211000Sxc151355 		bs.bs_dtimperiod = bs.bs_intval;
5221000Sxc151355 		bs.bs_nextdtim = nexttbtt;
5231000Sxc151355 
5241000Sxc151355 		/*
525*3147Sxc151355 		 * Setup the number of consecutive beacons to miss
526*3147Sxc151355 		 * before taking a BMISS interrupt.
5271000Sxc151355 		 * Note that we clamp the result to at most 10 beacons.
5281000Sxc151355 		 */
529*3147Sxc151355 		bs.bs_bmissthreshold = ic->ic_bmissthreshold;
5301000Sxc151355 		if (bs.bs_bmissthreshold > 10)
5311000Sxc151355 			bs.bs_bmissthreshold = 10;
5321000Sxc151355 		else if (bs.bs_bmissthreshold <= 0)
5331000Sxc151355 			bs.bs_bmissthreshold = 1;
5341000Sxc151355 		/*
5351000Sxc151355 		 * Calculate sleep duration.  The configuration is
5361000Sxc151355 		 * given in ms.  We insure a multiple of the beacon
5371000Sxc151355 		 * period is used.  Also, if the sleep duration is
5381000Sxc151355 		 * greater than the DTIM period then it makes senses
5391000Sxc151355 		 * to make it a multiple of that.
5401000Sxc151355 		 */
5411000Sxc151355 		bs.bs_sleepduration =
5421000Sxc151355 		    roundup((100 * 1000) / 1024, bs.bs_intval);
5431000Sxc151355 		if (bs.bs_sleepduration > bs.bs_dtimperiod)
5441000Sxc151355 			bs.bs_sleepduration =
5451000Sxc151355 			    roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
5461000Sxc151355 
5471000Sxc151355 
5481000Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_beacon_config(): "
5491000Sxc151355 		    "intval %u nexttbtt %u dtim %u"
5501000Sxc151355 		    " nextdtim %u bmiss %u sleep %u\n",
5511000Sxc151355 		    bs.bs_intval,
5521000Sxc151355 		    bs.bs_nexttbtt,
5531000Sxc151355 		    bs.bs_dtimperiod,
5541000Sxc151355 		    bs.bs_nextdtim,
5551000Sxc151355 		    bs.bs_bmissthreshold,
5561000Sxc151355 		    bs.bs_sleepduration));
5571000Sxc151355 		ATH_HAL_INTRSET(ah, 0);
5581000Sxc151355 		/*
5591000Sxc151355 		 * Reset our tsf so the hardware will update the
5601000Sxc151355 		 * tsf register to reflect timestamps found in
5611000Sxc151355 		 * received beacons.
5621000Sxc151355 		 */
5631000Sxc151355 		ATH_HAL_RESETTSF(ah);
5641000Sxc151355 		ATH_HAL_BEACONTIMERS(ah, &bs);
5651000Sxc151355 		asc->asc_imask |= HAL_INT_BMISS;
5661000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
5671000Sxc151355 	} else {
5681000Sxc151355 		ATH_HAL_INTRSET(ah, 0);
5691000Sxc151355 		ATH_HAL_BEACONINIT(ah, nexttbtt, in->in_intval);
5701000Sxc151355 		asc->asc_imask |= HAL_INT_SWBA;	/* beacon prepare */
5711000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
5721000Sxc151355 	}
5731000Sxc151355 }
5741000Sxc151355 
575*3147Sxc151355 /*
576*3147Sxc151355  * Allocate one or more key cache slots for a unicast key.  The
577*3147Sxc151355  * key itself is needed only to identify the cipher.  For hardware
578*3147Sxc151355  * TKIP with split cipher+MIC keys we allocate two key cache slot
579*3147Sxc151355  * pairs so that we can setup separate TX and RX MIC keys.  Note
580*3147Sxc151355  * that the MIC key for a TKIP key at slot i is assumed by the
581*3147Sxc151355  * hardware to be at slot i+64.  This limits TKIP keys to the first
582*3147Sxc151355  * 64 entries.
583*3147Sxc151355  */
584*3147Sxc151355 /* ARGSUSED */
585*3147Sxc151355 int
586*3147Sxc151355 ath_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
587*3147Sxc151355     ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
588*3147Sxc151355 {
589*3147Sxc151355 	*keyix = *rxkeyix = 0;
590*3147Sxc151355 	return (1);
591*3147Sxc151355 }
5921000Sxc151355 
593*3147Sxc151355 int
594*3147Sxc151355 ath_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
595*3147Sxc151355 {
596*3147Sxc151355 	struct ath_hal *ah = ((ath_t *)ic)->asc_ah;
597*3147Sxc151355 
598*3147Sxc151355 	ATH_HAL_KEYRESET(ah, k->wk_keyix);
599*3147Sxc151355 	return (1);
600*3147Sxc151355 }
6011000Sxc151355 
6021000Sxc151355 /*
603*3147Sxc151355  * Set the key cache contents for the specified key.  Key cache
604*3147Sxc151355  * slot(s) must already have been allocated by ath_key_alloc.
605*3147Sxc151355  */
606*3147Sxc151355 int
607*3147Sxc151355 ath_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
608*3147Sxc151355     const uint8_t mac[IEEE80211_ADDR_LEN])
609*3147Sxc151355 {
610*3147Sxc151355 	static const uint8_t ciphermap[] = {
611*3147Sxc151355 		HAL_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
612*3147Sxc151355 		HAL_CIPHER_TKIP,	/* IEEE80211_CIPHER_TKIP */
613*3147Sxc151355 		HAL_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
614*3147Sxc151355 		HAL_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
615*3147Sxc151355 		(uint8_t)-1,		/* 4 is not allocated */
616*3147Sxc151355 		HAL_CIPHER_CKIP,	/* IEEE80211_CIPHER_CKIP */
617*3147Sxc151355 		HAL_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
618*3147Sxc151355 	};
619*3147Sxc151355 	ath_t *asc = (ath_t *)ic;
620*3147Sxc151355 	struct ath_hal *ah = asc->asc_ah;
621*3147Sxc151355 	const struct ieee80211_cipher *cip = k->wk_cipher;
622*3147Sxc151355 	HAL_KEYVAL hk;
623*3147Sxc151355 
624*3147Sxc151355 	bzero(&hk, sizeof (hk));
625*3147Sxc151355 	/*
626*3147Sxc151355 	 * Software crypto uses a "clear key" so non-crypto
627*3147Sxc151355 	 * state kept in the key cache are maintainedd so that
628*3147Sxc151355 	 * rx frames have an entry to match.
629*3147Sxc151355 	 */
630*3147Sxc151355 	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
631*3147Sxc151355 		ASSERT(cip->ic_cipher < ATH_N(ciphermap));
632*3147Sxc151355 		hk.kv_type = ciphermap[cip->ic_cipher];
633*3147Sxc151355 		hk.kv_len = k->wk_keylen;
634*3147Sxc151355 		bcopy(k->wk_key, hk.kv_val, k->wk_keylen);
635*3147Sxc151355 	} else {
636*3147Sxc151355 		hk.kv_type = HAL_CIPHER_CLR;
637*3147Sxc151355 	}
638*3147Sxc151355 
639*3147Sxc151355 	return (ATH_HAL_KEYSET(ah, k->wk_keyix, &hk, mac));
640*3147Sxc151355 }
641*3147Sxc151355 
642*3147Sxc151355 /*
643*3147Sxc151355  * Enable/Disable short slot timing
6441000Sxc151355  */
6451000Sxc151355 void
646*3147Sxc151355 ath_set_shortslot(ieee80211com_t *ic, int onoff)
6471000Sxc151355 {
648*3147Sxc151355 	struct ath_hal *ah = ((ath_t *)ic)->asc_ah;
6491000Sxc151355 
650*3147Sxc151355 	if (onoff)
651*3147Sxc151355 		ATH_HAL_SETSLOTTIME(ah, HAL_SLOT_TIME_9);
652*3147Sxc151355 	else
653*3147Sxc151355 		ATH_HAL_SETSLOTTIME(ah, HAL_SLOT_TIME_20);
6541000Sxc151355 }
6551000Sxc151355 
656*3147Sxc151355 int
657*3147Sxc151355 ath_reset(ieee80211com_t *ic)
6581000Sxc151355 {
659*3147Sxc151355 	ath_t *asc = (ath_t *)ic;
6601000Sxc151355 	struct ath_hal *ah = asc->asc_ah;
661*3147Sxc151355 	struct ieee80211_channel *ch;
6621000Sxc151355 	HAL_STATUS status;
6631000Sxc151355 	HAL_CHANNEL hchan;
6641000Sxc151355 
6651000Sxc151355 	/*
6661000Sxc151355 	 * Convert to a HAL channel description with the flags
6671000Sxc151355 	 * constrained to reflect the current operating mode.
6681000Sxc151355 	 */
669*3147Sxc151355 	ch = ic->ic_curchan;
670*3147Sxc151355 	asc->asc_curchan.channel = ch->ich_freq;
671*3147Sxc151355 	asc->asc_curchan.channelFlags = ath_chan2flags(ic, ch);
6721000Sxc151355 
6731000Sxc151355 	ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
6741000Sxc151355 	ath_draintxq(asc);		/* stop xmit side */
675*3147Sxc151355 	if (ATH_IS_RUNNING(asc)) {
6761000Sxc151355 		ath_stoprecv(asc);		/* stop recv side */
677*3147Sxc151355 		/* indicate channel change so we do a full reset */
678*3147Sxc151355 		if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode, &hchan,
679*3147Sxc151355 		    AH_TRUE, &status)) {
680*3147Sxc151355 			ath_problem("ath: ath_reset(): "
681*3147Sxc151355 			    "resetting hardware failed, HAL status %u\n",
682*3147Sxc151355 			    status);
683*3147Sxc151355 		}
684*3147Sxc151355 		ath_chan_change(asc, ch);
6851000Sxc151355 	}
686*3147Sxc151355 	if (ATH_IS_RUNNING(asc)) {
6871000Sxc151355 		if (ath_startrecv(asc) != 0)	/* restart recv */
6881000Sxc151355 			ath_problem("ath: ath_reset(): "
6891000Sxc151355 			    "starting receiving logic failed\n");
690*3147Sxc151355 		if (ic->ic_state == IEEE80211_S_RUN) {
6911000Sxc151355 			ath_beacon_config(asc);	/* restart beacons */
6921000Sxc151355 		}
6931000Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
6941000Sxc151355 	}
695*3147Sxc151355 	return (0);
6961000Sxc151355 }
697