xref: /titanic_51/usr/src/uts/common/io/ath/ath_aux.c (revision 129d67acdc2d029d3d6cff4022c0c26c81c76f89)
17a1306a7Sxc151355 /*
23caf1114Sxc151355  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
37a1306a7Sxc151355  * Use is subject to license terms.
47a1306a7Sxc151355  */
57a1306a7Sxc151355 
67a1306a7Sxc151355 /*
77a1306a7Sxc151355  * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
87a1306a7Sxc151355  * All rights reserved.
97a1306a7Sxc151355  *
107a1306a7Sxc151355  * Redistribution and use in source and binary forms, with or without
117a1306a7Sxc151355  * modification, are permitted provided that the following conditions
127a1306a7Sxc151355  * are met:
137a1306a7Sxc151355  * 1. Redistributions of source code must retain the above copyright
147a1306a7Sxc151355  * notice, this list of conditions and the following disclaimer,
157a1306a7Sxc151355  * without modification.
167a1306a7Sxc151355  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
177a1306a7Sxc151355  * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
187a1306a7Sxc151355  * redistribution must be conditioned upon including a substantially
197a1306a7Sxc151355  * similar Disclaimer requirement for further binary redistribution.
207a1306a7Sxc151355  * 3. Neither the names of the above-listed copyright holders nor the names
217a1306a7Sxc151355  * of any contributors may be used to endorse or promote products derived
227a1306a7Sxc151355  * from this software without specific prior written permission.
237a1306a7Sxc151355  *
247a1306a7Sxc151355  * NO WARRANTY
257a1306a7Sxc151355  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
267a1306a7Sxc151355  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
277a1306a7Sxc151355  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
287a1306a7Sxc151355  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
297a1306a7Sxc151355  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
307a1306a7Sxc151355  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
317a1306a7Sxc151355  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
327a1306a7Sxc151355  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
337a1306a7Sxc151355  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
347a1306a7Sxc151355  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
357a1306a7Sxc151355  * THE POSSIBILITY OF SUCH DAMAGES.
367a1306a7Sxc151355  */
377a1306a7Sxc151355 
387a1306a7Sxc151355 #include <sys/param.h>
397a1306a7Sxc151355 #include <sys/types.h>
407a1306a7Sxc151355 #include <sys/signal.h>
417a1306a7Sxc151355 #include <sys/stream.h>
427a1306a7Sxc151355 #include <sys/termio.h>
437a1306a7Sxc151355 #include <sys/errno.h>
447a1306a7Sxc151355 #include <sys/file.h>
457a1306a7Sxc151355 #include <sys/cmn_err.h>
467a1306a7Sxc151355 #include <sys/stropts.h>
477a1306a7Sxc151355 #include <sys/strsubr.h>
487a1306a7Sxc151355 #include <sys/strtty.h>
497a1306a7Sxc151355 #include <sys/kbio.h>
507a1306a7Sxc151355 #include <sys/cred.h>
517a1306a7Sxc151355 #include <sys/stat.h>
527a1306a7Sxc151355 #include <sys/consdev.h>
537a1306a7Sxc151355 #include <sys/kmem.h>
547a1306a7Sxc151355 #include <sys/modctl.h>
557a1306a7Sxc151355 #include <sys/ddi.h>
567a1306a7Sxc151355 #include <sys/sunddi.h>
577a1306a7Sxc151355 #include <sys/pci.h>
587a1306a7Sxc151355 #include <sys/errno.h>
597a1306a7Sxc151355 #include <sys/gld.h>
607a1306a7Sxc151355 #include <sys/dlpi.h>
617a1306a7Sxc151355 #include <sys/ethernet.h>
627a1306a7Sxc151355 #include <sys/list.h>
637a1306a7Sxc151355 #include <sys/byteorder.h>
647a1306a7Sxc151355 #include <sys/strsun.h>
657a1306a7Sxc151355 #include <inet/common.h>
667a1306a7Sxc151355 #include <inet/nd.h>
677a1306a7Sxc151355 #include <inet/mi.h>
687a1306a7Sxc151355 #include <inet/wifi_ioctl.h>
697a1306a7Sxc151355 #include "ath_hal.h"
707a1306a7Sxc151355 #include "ath_impl.h"
717a1306a7Sxc151355 
727a1306a7Sxc151355 static const char *acnames[] = {
737a1306a7Sxc151355 	"WME_AC_BE",
747a1306a7Sxc151355 	"WME_AC_BK",
757a1306a7Sxc151355 	"WME_AC_VI",
767a1306a7Sxc151355 	"WME_AC_VO",
777a1306a7Sxc151355 	"WME_UPSD"
787a1306a7Sxc151355 };
797a1306a7Sxc151355 
807a1306a7Sxc151355 extern void ath_setup_desc(ath_t *asc, struct ath_buf *bf);
817a1306a7Sxc151355 
823caf1114Sxc151355 
833caf1114Sxc151355 const char *
843caf1114Sxc151355 ath_get_hal_status_desc(HAL_STATUS status)
853caf1114Sxc151355 {
863caf1114Sxc151355 	static const char *hal_status_desc[] = {
873caf1114Sxc151355 	    "No error",
883caf1114Sxc151355 	    "No hardware present or device not yet supported",
893caf1114Sxc151355 	    "Memory allocation failed",
903caf1114Sxc151355 	    "Hardware didn't respond as expected",
913caf1114Sxc151355 	    "EEPROM magic number invalid",
923caf1114Sxc151355 	    "EEPROM version invalid",
933caf1114Sxc151355 	    "EEPROM unreadable",
943caf1114Sxc151355 	    "EEPROM checksum invalid",
953caf1114Sxc151355 	    "EEPROM read problem",
963caf1114Sxc151355 	    "EEPROM mac address invalid",
973caf1114Sxc151355 	    "EEPROM size not supported",
983caf1114Sxc151355 	    "Attempt to change write-locked EEPROM",
993caf1114Sxc151355 	    "Invalid parameter to function",
1003caf1114Sxc151355 	    "Hardware revision not supported",
1013caf1114Sxc151355 	    "Hardware self-test failed",
1023caf1114Sxc151355 	    "Operation incomplete"
1033caf1114Sxc151355 	};
1043caf1114Sxc151355 
1053caf1114Sxc151355 	if (status >= 0 && status < sizeof (hal_status_desc)/sizeof (char *))
1063caf1114Sxc151355 		return (hal_status_desc[status]);
1073caf1114Sxc151355 	else
1083caf1114Sxc151355 		return ("");
1093caf1114Sxc151355 }
1103caf1114Sxc151355 
1117a1306a7Sxc151355 uint32_t
1127a1306a7Sxc151355 ath_calcrxfilter(ath_t *asc)
1137a1306a7Sxc151355 {
1140ba2cbe9Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
1157a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
1167a1306a7Sxc151355 	uint32_t rfilt;
1177a1306a7Sxc151355 
1187a1306a7Sxc151355 	rfilt = (ATH_HAL_GETRXFILTER(ah) & HAL_RX_FILTER_PHYERR)
1197a1306a7Sxc151355 	    | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
1200ba2cbe9Sxc151355 	if (ic->ic_opmode != IEEE80211_M_STA)
1217a1306a7Sxc151355 		rfilt |= HAL_RX_FILTER_PROBEREQ;
1223caf1114Sxc151355 	if (ic->ic_opmode != IEEE80211_M_HOSTAP && asc->asc_promisc)
1233caf1114Sxc151355 		rfilt |= HAL_RX_FILTER_PROM;	/* promiscuous */
1240ba2cbe9Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA ||
1250ba2cbe9Sxc151355 	    ic->ic_opmode == IEEE80211_M_IBSS ||
1260ba2cbe9Sxc151355 	    ic->ic_state == IEEE80211_S_SCAN)
1277a1306a7Sxc151355 		rfilt |= HAL_RX_FILTER_BEACON;
1287a1306a7Sxc151355 	return (rfilt);
1297a1306a7Sxc151355 }
1307a1306a7Sxc151355 
1317a1306a7Sxc151355 static int
1327a1306a7Sxc151355 ath_set_data_queue(ath_t *asc, int ac, int haltype)
1337a1306a7Sxc151355 {
1347a1306a7Sxc151355 	HAL_TXQ_INFO qi;
1357a1306a7Sxc151355 	int qnum;
1367a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
1377a1306a7Sxc151355 	struct ath_txq *txq;
1387a1306a7Sxc151355 
1397a1306a7Sxc151355 	if (ac >= ATH_N(asc->asc_ac2q)) {
1407a1306a7Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
1417a1306a7Sxc151355 		    "ac %u out of range, max %u!\n",
1427a1306a7Sxc151355 		    ac, ATH_N(asc->asc_ac2q)));
1437a1306a7Sxc151355 		return (1);
1447a1306a7Sxc151355 	}
1457a1306a7Sxc151355 	(void) memset(&qi, 0, sizeof (qi));
1467a1306a7Sxc151355 	qi.tqi_subtype = haltype;
1477a1306a7Sxc151355 	/*
1487a1306a7Sxc151355 	 * Enable interrupts only for EOL and DESC conditions.
1497a1306a7Sxc151355 	 * We mark tx descriptors to receive a DESC interrupt
1507a1306a7Sxc151355 	 * when a tx queue gets deep; otherwise waiting for the
1517a1306a7Sxc151355 	 * EOL to reap descriptors.  Note that this is done to
1527a1306a7Sxc151355 	 * reduce interrupt load and this only defers reaping
1537a1306a7Sxc151355 	 * descriptors, never transmitting frames.  Aside from
1547a1306a7Sxc151355 	 * reducing interrupts this also permits more concurrency.
1557a1306a7Sxc151355 	 * The only potential downside is if the tx queue backs
1567a1306a7Sxc151355 	 * up in which case the top half of the kernel may backup
1577a1306a7Sxc151355 	 * due to a lack of tx descriptors.
1587a1306a7Sxc151355 	 */
1590ba2cbe9Sxc151355 	qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE;
1607a1306a7Sxc151355 	qnum = ATH_HAL_SETUPTXQUEUE(ah, HAL_TX_QUEUE_DATA, &qi);
1617a1306a7Sxc151355 	if (qnum == -1) {
1627a1306a7Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
1637a1306a7Sxc151355 		    "Unable to setup hardware queue for %s traffic!\n",
1647a1306a7Sxc151355 		    acnames[ac]));
1657a1306a7Sxc151355 		return (1);
1667a1306a7Sxc151355 	}
1677a1306a7Sxc151355 	if (qnum >= ATH_N(asc->asc_txq)) {
1687a1306a7Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
1697a1306a7Sxc151355 		    "hal qnum %u out of range, max %u!\n",
1707a1306a7Sxc151355 		    qnum, ATH_N(asc->asc_txq)));
1717a1306a7Sxc151355 		return (1);
1727a1306a7Sxc151355 	}
1737a1306a7Sxc151355 	if (!ATH_TXQ_SETUP(asc, qnum)) {
1747a1306a7Sxc151355 		txq = &asc->asc_txq[qnum];
1757a1306a7Sxc151355 		txq->axq_qnum = qnum;
1767a1306a7Sxc151355 		txq->axq_depth = 0;
1777a1306a7Sxc151355 		txq->axq_intrcnt = 0;
1787a1306a7Sxc151355 		txq->axq_link = NULL;
1797a1306a7Sxc151355 		list_create(&txq->axq_list, sizeof (struct ath_buf),
1807a1306a7Sxc151355 		    offsetof(struct ath_buf, bf_node));
1817a1306a7Sxc151355 		mutex_init(&txq->axq_lock, NULL, MUTEX_DRIVER, NULL);
1827a1306a7Sxc151355 		asc->asc_txqsetup |= 1<<qnum;
1837a1306a7Sxc151355 	}
1847a1306a7Sxc151355 	asc->asc_ac2q[ac] = &asc->asc_txq[qnum];
1857a1306a7Sxc151355 	return (0);
1867a1306a7Sxc151355 }
1877a1306a7Sxc151355 
1887a1306a7Sxc151355 int
1897a1306a7Sxc151355 ath_txq_setup(ath_t *asc)
1907a1306a7Sxc151355 {
1917a1306a7Sxc151355 	if (ath_set_data_queue(asc, WME_AC_BE, HAL_WME_AC_BK) ||
1927a1306a7Sxc151355 	    ath_set_data_queue(asc, WME_AC_BK, HAL_WME_AC_BE) ||
1937a1306a7Sxc151355 	    ath_set_data_queue(asc, WME_AC_VI, HAL_WME_AC_VI) ||
1947a1306a7Sxc151355 	    ath_set_data_queue(asc, WME_AC_VO, HAL_WME_AC_VO)) {
1957a1306a7Sxc151355 		return (1);
1967a1306a7Sxc151355 	}
1977a1306a7Sxc151355 
1987a1306a7Sxc151355 	return (0);
1997a1306a7Sxc151355 }
2007a1306a7Sxc151355 
2017a1306a7Sxc151355 void
2020ba2cbe9Sxc151355 ath_txq_cleanup(ath_t *asc)
2030ba2cbe9Sxc151355 {
2040ba2cbe9Sxc151355 	int i;
2050ba2cbe9Sxc151355 
2060ba2cbe9Sxc151355 	mutex_destroy(&asc->asc_txbuflock);
2070ba2cbe9Sxc151355 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
2080ba2cbe9Sxc151355 		if (ATH_TXQ_SETUP(asc, i)) {
2090ba2cbe9Sxc151355 			struct ath_txq *txq = &asc->asc_txq[i];
2100ba2cbe9Sxc151355 
2110ba2cbe9Sxc151355 			ATH_HAL_RELEASETXQUEUE(asc->asc_ah, txq->axq_qnum);
2120ba2cbe9Sxc151355 			mutex_destroy(&txq->axq_lock);
2130ba2cbe9Sxc151355 			asc->asc_txqsetup &= ~(1 << txq->axq_qnum);
2140ba2cbe9Sxc151355 		}
2150ba2cbe9Sxc151355 	}
2160ba2cbe9Sxc151355 }
2170ba2cbe9Sxc151355 
2180ba2cbe9Sxc151355 void
2197a1306a7Sxc151355 ath_setcurmode(ath_t *asc, enum ieee80211_phymode mode)
2207a1306a7Sxc151355 {
2217a1306a7Sxc151355 	const HAL_RATE_TABLE *rt;
2227a1306a7Sxc151355 	int i;
2237a1306a7Sxc151355 
2247a1306a7Sxc151355 	for (i = 0; i < sizeof (asc->asc_rixmap); i++)
2257a1306a7Sxc151355 		asc->asc_rixmap[i] = 0xff;
2267a1306a7Sxc151355 
2277a1306a7Sxc151355 	rt = asc->asc_rates[mode];
2287a1306a7Sxc151355 	ASSERT(rt != NULL);
2297a1306a7Sxc151355 
2307a1306a7Sxc151355 	for (i = 0; i < rt->rateCount; i++)
2315d2225f6Sff224033 		asc->asc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] =
2325d2225f6Sff224033 		    (uint8_t)i;
2337a1306a7Sxc151355 
2347a1306a7Sxc151355 	asc->asc_currates = rt;
2357a1306a7Sxc151355 	asc->asc_curmode = mode;
236*129d67acSlin wang - Sun Microsystems - Beijing China 
237*129d67acSlin wang - Sun Microsystems - Beijing China 	/*
238*129d67acSlin wang - Sun Microsystems - Beijing China 	 * All protection frames are transmitted at 2Mb/s for
239*129d67acSlin wang - Sun Microsystems - Beijing China 	 * 11g, otherwise at 1Mb/s.
240*129d67acSlin wang - Sun Microsystems - Beijing China 	 * select protection rate index from rate table.
241*129d67acSlin wang - Sun Microsystems - Beijing China 	 */
242*129d67acSlin wang - Sun Microsystems - Beijing China 	asc->asc_protrix = (mode == IEEE80211_MODE_11G ? 1 : 0);
2437a1306a7Sxc151355 }
2447a1306a7Sxc151355 
2457a1306a7Sxc151355 /* Set correct parameters for a certain mode */
2467a1306a7Sxc151355 void
2477a1306a7Sxc151355 ath_mode_init(ath_t *asc)
2487a1306a7Sxc151355 {
2490ba2cbe9Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
2507a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
2517a1306a7Sxc151355 	uint32_t rfilt;
2527a1306a7Sxc151355 
2537a1306a7Sxc151355 	/* configure rx filter */
2547a1306a7Sxc151355 	rfilt = ath_calcrxfilter(asc);
2557a1306a7Sxc151355 	ATH_HAL_SETRXFILTER(ah, rfilt);
2567a1306a7Sxc151355 	ATH_HAL_SETOPMODE(ah);
2573caf1114Sxc151355 	ATH_HAL_SETMCASTFILTER(ah, asc->asc_mcast_hash[0],
2583caf1114Sxc151355 	    asc->asc_mcast_hash[1]);
2597a1306a7Sxc151355 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_mode_init(): "
2607a1306a7Sxc151355 	    "mode =%d RX filter 0x%x, MC filter %08x:%08x\n",
2610ba2cbe9Sxc151355 	    ic->ic_opmode, rfilt,
2623caf1114Sxc151355 	    asc->asc_mcast_hash[0], asc->asc_mcast_hash[1]));
2637a1306a7Sxc151355 }
2647a1306a7Sxc151355 
2657a1306a7Sxc151355 /*
2667a1306a7Sxc151355  * Disable the receive h/w in preparation for a reset.
2677a1306a7Sxc151355  */
2687a1306a7Sxc151355 void
2697a1306a7Sxc151355 ath_stoprecv(ath_t *asc)
2707a1306a7Sxc151355 {
2717a1306a7Sxc151355 	ATH_HAL_STOPPCURECV(asc->asc_ah);	/* disable PCU */
2727a1306a7Sxc151355 	ATH_HAL_SETRXFILTER(asc->asc_ah, 0);	/* clear recv filter */
2737a1306a7Sxc151355 	ATH_HAL_STOPDMARECV(asc->asc_ah);	/* disable DMA engine */
2747a1306a7Sxc151355 	drv_usecwait(3000);
2757a1306a7Sxc151355 
2767a1306a7Sxc151355 	ATH_DEBUG((ATH_DBG_AUX, "ath: ath_stoprecv(): rx queue %p, link %p\n",
2777a1306a7Sxc151355 	    ATH_HAL_GETRXBUF(asc->asc_ah), asc->asc_rxlink));
2787a1306a7Sxc151355 	asc->asc_rxlink = NULL;
2797a1306a7Sxc151355 }
2807a1306a7Sxc151355 
2817a1306a7Sxc151355 uint32_t
2820ba2cbe9Sxc151355 ath_chan2flags(ieee80211com_t *isc, struct ieee80211_channel *chan)
2837a1306a7Sxc151355 {
2847a1306a7Sxc151355 	static const uint32_t modeflags[] = {
2857a1306a7Sxc151355 	    0,				/* IEEE80211_MODE_AUTO */
2867a1306a7Sxc151355 	    CHANNEL_A,			/* IEEE80211_MODE_11A */
2877a1306a7Sxc151355 	    CHANNEL_B,			/* IEEE80211_MODE_11B */
2887a1306a7Sxc151355 	    CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
2890ba2cbe9Sxc151355 	    0,				/* IEEE80211_MODE_FH */
2900ba2cbe9Sxc151355 	    CHANNEL_108A,		/* IEEE80211_MODE_TURBO_A */
2910ba2cbe9Sxc151355 	    CHANNEL_108G		/* IEEE80211_MODE_TURBO_G */
2927a1306a7Sxc151355 	};
2937a1306a7Sxc151355 	return (modeflags[ieee80211_chan2mode(isc, chan)]);
2947a1306a7Sxc151355 }
2957a1306a7Sxc151355 
2967a1306a7Sxc151355 
2977a1306a7Sxc151355 int
2987a1306a7Sxc151355 ath_getchannels(ath_t *asc, uint32_t cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
2997a1306a7Sxc151355 {
3000ba2cbe9Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
3017a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
3027a1306a7Sxc151355 	HAL_CHANNEL *chans;
3037a1306a7Sxc151355 	int i, ix;
3047a1306a7Sxc151355 	uint32_t nchan;
3057a1306a7Sxc151355 
3067a1306a7Sxc151355 	chans = (HAL_CHANNEL *)
3077a1306a7Sxc151355 	    kmem_zalloc(IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL), KM_SLEEP);
3087a1306a7Sxc151355 
3097a1306a7Sxc151355 	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
3100ba2cbe9Sxc151355 	    NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) {
3117a1306a7Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
3123caf1114Sxc151355 		    "unable to get channel list\n"));
3133caf1114Sxc151355 		kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
3147a1306a7Sxc151355 		return (EINVAL);
3157a1306a7Sxc151355 	}
3167a1306a7Sxc151355 
3177a1306a7Sxc151355 	/*
3187a1306a7Sxc151355 	 * Convert HAL channels to ieee80211 ones and insert
3197a1306a7Sxc151355 	 * them in the table according to their channel number.
3207a1306a7Sxc151355 	 */
3217a1306a7Sxc151355 	for (i = 0; i < nchan; i++) {
3227a1306a7Sxc151355 		HAL_CHANNEL *c = &chans[i];
3230ba2cbe9Sxc151355 		uint16_t flags;
3240ba2cbe9Sxc151355 		ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags);
3257a1306a7Sxc151355 		if (ix > IEEE80211_CHAN_MAX) {
3267a1306a7Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
3270ba2cbe9Sxc151355 			    "bad hal channel %d (%u/%x) ignored\n",
3287a1306a7Sxc151355 			    ix, c->channel, c->channelFlags));
3297a1306a7Sxc151355 			continue;
3307a1306a7Sxc151355 		}
3317a1306a7Sxc151355 		/* NB: flags are known to be compatible */
3320ba2cbe9Sxc151355 		if (ix < 0) {
3330ba2cbe9Sxc151355 			/*
3340ba2cbe9Sxc151355 			 * can't handle frequency <2400MHz (negative
3350ba2cbe9Sxc151355 			 * channels) right now
3360ba2cbe9Sxc151355 			 */
3370ba2cbe9Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath:ath_getchannels(): "
3380ba2cbe9Sxc151355 			    "hal channel %d (%u/%x) "
3390ba2cbe9Sxc151355 			    "cannot be handled, ignored\n",
3400ba2cbe9Sxc151355 			    ix, c->channel, c->channelFlags));
3410ba2cbe9Sxc151355 			continue;
3420ba2cbe9Sxc151355 		}
3430ba2cbe9Sxc151355 		/*
3440ba2cbe9Sxc151355 		 * Calculate net80211 flags; most are compatible
3450ba2cbe9Sxc151355 		 * but some need massaging.  Note the static turbo
3460ba2cbe9Sxc151355 		 * conversion can be removed once net80211 is updated
3470ba2cbe9Sxc151355 		 * to understand static vs. dynamic turbo.
3480ba2cbe9Sxc151355 		 */
3490ba2cbe9Sxc151355 		flags = c->channelFlags & CHANNEL_COMPAT;
3500ba2cbe9Sxc151355 		if (c->channelFlags & CHANNEL_STURBO)
3510ba2cbe9Sxc151355 			flags |= IEEE80211_CHAN_TURBO;
3520ba2cbe9Sxc151355 		if (ic->ic_sup_channels[ix].ich_freq == 0) {
3530ba2cbe9Sxc151355 			ic->ic_sup_channels[ix].ich_freq = c->channel;
3540ba2cbe9Sxc151355 			ic->ic_sup_channels[ix].ich_flags = flags;
3557a1306a7Sxc151355 		} else {
3567a1306a7Sxc151355 			/* channels overlap; e.g. 11g and 11b */
3570ba2cbe9Sxc151355 			ic->ic_sup_channels[ix].ich_flags |= flags;
3587a1306a7Sxc151355 		}
3597a1306a7Sxc151355 		if ((c->channelFlags & CHANNEL_G) == CHANNEL_G)
3607a1306a7Sxc151355 			asc->asc_have11g = 1;
3617a1306a7Sxc151355 	}
3627a1306a7Sxc151355 	kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
3637a1306a7Sxc151355 	return (0);
3647a1306a7Sxc151355 }
3657a1306a7Sxc151355 
3667a1306a7Sxc151355 static void
3677a1306a7Sxc151355 ath_drainq(ath_t *asc, struct ath_txq *txq)
3687a1306a7Sxc151355 {
3697a1306a7Sxc151355 	struct ath_buf *bf;
3707a1306a7Sxc151355 
3717a1306a7Sxc151355 	/*
3727a1306a7Sxc151355 	 * This assumes output has been stopped.
3737a1306a7Sxc151355 	 */
3747a1306a7Sxc151355 	for (;;) {
3757a1306a7Sxc151355 		mutex_enter(&txq->axq_lock);
3767a1306a7Sxc151355 		bf = list_head(&txq->axq_list);
3777a1306a7Sxc151355 		if (bf == NULL) {
3787a1306a7Sxc151355 			txq->axq_link = NULL;
3797a1306a7Sxc151355 			mutex_exit(&txq->axq_lock);
3807a1306a7Sxc151355 			break;
3817a1306a7Sxc151355 		}
3827a1306a7Sxc151355 		list_remove(&txq->axq_list, bf);
3837a1306a7Sxc151355 		mutex_exit(&txq->axq_lock);
3847a1306a7Sxc151355 		bf->bf_in = NULL;
3857a1306a7Sxc151355 		mutex_enter(&asc->asc_txbuflock);
3867a1306a7Sxc151355 		list_insert_tail(&asc->asc_txbuf_list, bf);
3877a1306a7Sxc151355 		mutex_exit(&asc->asc_txbuflock);
3887a1306a7Sxc151355 	}
3897a1306a7Sxc151355 }
3907a1306a7Sxc151355 
3917a1306a7Sxc151355 
3927a1306a7Sxc151355 /*
3937a1306a7Sxc151355  * Drain the transmit queues and reclaim resources.
3947a1306a7Sxc151355  */
3957a1306a7Sxc151355 void
3967a1306a7Sxc151355 ath_draintxq(ath_t *asc)
3977a1306a7Sxc151355 {
3987a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
3997a1306a7Sxc151355 	struct ath_txq *txq;
4007a1306a7Sxc151355 	int i;
4017a1306a7Sxc151355 
4027a1306a7Sxc151355 	if (!asc->asc_invalid) {
4037a1306a7Sxc151355 		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
4047a1306a7Sxc151355 			if (ATH_TXQ_SETUP(asc, i)) {
4057a1306a7Sxc151355 				txq = &asc->asc_txq[i];
4067a1306a7Sxc151355 				(void) ATH_HAL_STOPTXDMA(ah, txq->axq_qnum);
4077a1306a7Sxc151355 			}
4087a1306a7Sxc151355 		}
4097a1306a7Sxc151355 	}
4107a1306a7Sxc151355 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
4117a1306a7Sxc151355 		if (ATH_TXQ_SETUP(asc, i)) {
4127a1306a7Sxc151355 			ath_drainq(asc, &asc->asc_txq[i]);
4137a1306a7Sxc151355 		}
4147a1306a7Sxc151355 	}
4157a1306a7Sxc151355 }
4167a1306a7Sxc151355 
4177a1306a7Sxc151355 
4187a1306a7Sxc151355 /* Enable the receive h/w following a reset */
4197a1306a7Sxc151355 int
4207a1306a7Sxc151355 ath_startrecv(ath_t *asc)
4217a1306a7Sxc151355 {
4227a1306a7Sxc151355 	struct ath_buf *bf;
4237a1306a7Sxc151355 
4247a1306a7Sxc151355 	asc->asc_rxlink = NULL;
4257a1306a7Sxc151355 
4267a1306a7Sxc151355 	bf = list_head(&asc->asc_rxbuf_list);
4277a1306a7Sxc151355 	while (bf != NULL) {
4287a1306a7Sxc151355 		ath_setup_desc(asc, bf);
4297a1306a7Sxc151355 		bf = list_next(&asc->asc_rxbuf_list, bf);
4307a1306a7Sxc151355 	}
4317a1306a7Sxc151355 
4327a1306a7Sxc151355 	bf = list_head(&asc->asc_rxbuf_list);
4337a1306a7Sxc151355 	ATH_HAL_PUTRXBUF(asc->asc_ah, bf->bf_daddr);
4347a1306a7Sxc151355 	ATH_HAL_RXENA(asc->asc_ah);		/* enable recv descriptors */
4357a1306a7Sxc151355 	ath_mode_init(asc);			/* set filters, etc. */
4367a1306a7Sxc151355 	ATH_HAL_STARTPCURECV(asc->asc_ah);	/* re-enable PCU/DMA engine */
4377a1306a7Sxc151355 	return (0);
4387a1306a7Sxc151355 }
4397a1306a7Sxc151355 
4407a1306a7Sxc151355 /*
4410ba2cbe9Sxc151355  * Update internal state after a channel change.
4420ba2cbe9Sxc151355  */
4430ba2cbe9Sxc151355 void
4440ba2cbe9Sxc151355 ath_chan_change(ath_t *asc, struct ieee80211_channel *chan)
4450ba2cbe9Sxc151355 {
4460ba2cbe9Sxc151355 	struct ieee80211com *ic = &asc->asc_isc;
4470ba2cbe9Sxc151355 	enum ieee80211_phymode mode;
4480ba2cbe9Sxc151355 
4490ba2cbe9Sxc151355 	/*
4500ba2cbe9Sxc151355 	 * Change channels and update the h/w rate map
4510ba2cbe9Sxc151355 	 * if we're switching; e.g. 11a to 11b/g.
4520ba2cbe9Sxc151355 	 */
4530ba2cbe9Sxc151355 	mode = ieee80211_chan2mode(ic, chan);
4540ba2cbe9Sxc151355 	if (mode != asc->asc_curmode)
4550ba2cbe9Sxc151355 		ath_setcurmode(asc, mode);
4560ba2cbe9Sxc151355 }
4570ba2cbe9Sxc151355 
4580ba2cbe9Sxc151355 /*
4597a1306a7Sxc151355  * Set/change channels.  If the channel is really being changed,
4607a1306a7Sxc151355  * it's done by resetting the chip.  To accomplish this we must
4617a1306a7Sxc151355  * first cleanup any pending DMA.
4627a1306a7Sxc151355  */
4637a1306a7Sxc151355 int
4640ba2cbe9Sxc151355 ath_chan_set(ath_t *asc, struct ieee80211_channel *chan)
4657a1306a7Sxc151355 {
4667a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
4670ba2cbe9Sxc151355 	ieee80211com_t *ic = &asc->asc_isc;
4687a1306a7Sxc151355 
4690ba2cbe9Sxc151355 	if (chan != ic->ic_ibss_chan) {
4707a1306a7Sxc151355 		HAL_STATUS status;
4717a1306a7Sxc151355 		HAL_CHANNEL hchan;
4727a1306a7Sxc151355 
4737a1306a7Sxc151355 		/*
4747a1306a7Sxc151355 		 * To switch channels clear any pending DMA operations;
4757a1306a7Sxc151355 		 * wait long enough for the RX fifo to drain, reset the
4767a1306a7Sxc151355 		 * hardware at the new frequency, and then re-enable
4777a1306a7Sxc151355 		 * the relevant bits of the h/w.
4787a1306a7Sxc151355 		 */
4797a1306a7Sxc151355 		ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
4807a1306a7Sxc151355 		ath_draintxq(asc);		/* clear pending tx frames */
4817a1306a7Sxc151355 		ath_stoprecv(asc);		/* turn off frame recv */
4827a1306a7Sxc151355 		/*
4837a1306a7Sxc151355 		 * Convert to a HAL channel description with
4847a1306a7Sxc151355 		 * the flags constrained to reflect the current
4857a1306a7Sxc151355 		 * operating mode.
4867a1306a7Sxc151355 		 */
4877a1306a7Sxc151355 		hchan.channel = chan->ich_freq;
4880ba2cbe9Sxc151355 		hchan.channelFlags = ath_chan2flags(ic, chan);
4890ba2cbe9Sxc151355 		if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
4907a1306a7Sxc151355 		    &hchan, AH_TRUE, &status)) {
4917a1306a7Sxc151355 			ATH_DEBUG((ATH_DBG_AUX, "ath: ath_chan_set():"
4923caf1114Sxc151355 			    "unable to reset channel %u (%uMhz)\n"
4933caf1114Sxc151355 			    "flags 0x%x: '%s' (HAL status %u)\n",
4943caf1114Sxc151355 			    ieee80211_chan2ieee(ic, chan), hchan.channel,
4953caf1114Sxc151355 			    hchan.channelFlags,
4963caf1114Sxc151355 			    ath_get_hal_status_desc(status), status));
4977a1306a7Sxc151355 			return (EIO);
4987a1306a7Sxc151355 		}
4990ba2cbe9Sxc151355 		asc->asc_curchan = hchan;
5007a1306a7Sxc151355 
5017a1306a7Sxc151355 		/*
5027a1306a7Sxc151355 		 * Re-enable rx framework.
5037a1306a7Sxc151355 		 */
5047a1306a7Sxc151355 		if (ath_startrecv(asc) != 0) {
5057a1306a7Sxc151355 			ath_problem("ath: ath_chan_set(): "
5067a1306a7Sxc151355 			    "restarting receiving logic failed\n");
5077a1306a7Sxc151355 			return (EIO);
5087a1306a7Sxc151355 		}
5097a1306a7Sxc151355 
5107a1306a7Sxc151355 		/*
5117a1306a7Sxc151355 		 * Change channels and update the h/w rate map
5127a1306a7Sxc151355 		 * if we're switching; e.g. 11a to 11b/g.
5137a1306a7Sxc151355 		 */
5140ba2cbe9Sxc151355 		ic->ic_ibss_chan = chan;
5150ba2cbe9Sxc151355 		ath_chan_change(asc, chan);
5167a1306a7Sxc151355 		/*
5177a1306a7Sxc151355 		 * Re-enable interrupts.
5187a1306a7Sxc151355 		 */
5197a1306a7Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
5207a1306a7Sxc151355 	}
5217a1306a7Sxc151355 	return (0);
5227a1306a7Sxc151355 }
5237a1306a7Sxc151355 
5247a1306a7Sxc151355 
5257a1306a7Sxc151355 /*
5267a1306a7Sxc151355  * Configure the beacon and sleep timers.
5277a1306a7Sxc151355  *
5287a1306a7Sxc151355  * When operating as an AP this resets the TSF and sets
5297a1306a7Sxc151355  * up the hardware to notify us when we need to issue beacons.
5307a1306a7Sxc151355  *
5317a1306a7Sxc151355  * When operating in station mode this sets up the beacon
5327a1306a7Sxc151355  * timers according to the timestamp of the last received
5337a1306a7Sxc151355  * beacon and the current TSF, configures PCF and DTIM
5347a1306a7Sxc151355  * handling, programs the sleep registers so the hardware
5357a1306a7Sxc151355  * will wakeup in time to receive beacons, and configures
5367a1306a7Sxc151355  * the beacon miss handling so we'll receive a BMISS
5377a1306a7Sxc151355  * interrupt when we stop seeing beacons from the AP
5387a1306a7Sxc151355  * we've associated with.
5397a1306a7Sxc151355  */
5407a1306a7Sxc151355 void
5417a1306a7Sxc151355 ath_beacon_config(ath_t *asc)
5427a1306a7Sxc151355 {
5437a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
5440ba2cbe9Sxc151355 	ieee80211com_t *ic = (ieee80211com_t *)asc;
5450ba2cbe9Sxc151355 	struct ieee80211_node *in = ic->ic_bss;
5467a1306a7Sxc151355 	uint32_t nexttbtt;
5477a1306a7Sxc151355 
548*129d67acSlin wang - Sun Microsystems - Beijing China 	/* extract tstamp from last beacon and convert to TU */
5490ba2cbe9Sxc151355 	nexttbtt = (ATH_LE_READ_4(in->in_tstamp.data + 4) << 22) |
5500ba2cbe9Sxc151355 	    (ATH_LE_READ_4(in->in_tstamp.data) >> 10);
5517a1306a7Sxc151355 	nexttbtt += in->in_intval;
5520ba2cbe9Sxc151355 	if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
5537a1306a7Sxc151355 		HAL_BEACON_STATE bs;
5547a1306a7Sxc151355 
5557a1306a7Sxc151355 		/* NB: no PCF support right now */
5567a1306a7Sxc151355 		bzero(&bs, sizeof (bs));
5577a1306a7Sxc151355 		bs.bs_intval = in->in_intval;
5587a1306a7Sxc151355 		bs.bs_nexttbtt = nexttbtt;
5597a1306a7Sxc151355 		bs.bs_dtimperiod = bs.bs_intval;
5607a1306a7Sxc151355 		bs.bs_nextdtim = nexttbtt;
5617a1306a7Sxc151355 
5627a1306a7Sxc151355 		/*
5630ba2cbe9Sxc151355 		 * Setup the number of consecutive beacons to miss
5640ba2cbe9Sxc151355 		 * before taking a BMISS interrupt.
5657a1306a7Sxc151355 		 * Note that we clamp the result to at most 10 beacons.
5667a1306a7Sxc151355 		 */
5670ba2cbe9Sxc151355 		bs.bs_bmissthreshold = ic->ic_bmissthreshold;
5687a1306a7Sxc151355 		if (bs.bs_bmissthreshold > 10)
5697a1306a7Sxc151355 			bs.bs_bmissthreshold = 10;
57022eb7cb5Sgd78059 		else if (bs.bs_bmissthreshold < 1)
5717a1306a7Sxc151355 			bs.bs_bmissthreshold = 1;
5727a1306a7Sxc151355 		/*
5737a1306a7Sxc151355 		 * Calculate sleep duration.  The configuration is
5747a1306a7Sxc151355 		 * given in ms.  We insure a multiple of the beacon
5757a1306a7Sxc151355 		 * period is used.  Also, if the sleep duration is
5767a1306a7Sxc151355 		 * greater than the DTIM period then it makes senses
5777a1306a7Sxc151355 		 * to make it a multiple of that.
5787a1306a7Sxc151355 		 */
5797a1306a7Sxc151355 		bs.bs_sleepduration =
5807a1306a7Sxc151355 		    roundup((100 * 1000) / 1024, bs.bs_intval);
5817a1306a7Sxc151355 		if (bs.bs_sleepduration > bs.bs_dtimperiod)
5827a1306a7Sxc151355 			bs.bs_sleepduration =
5837a1306a7Sxc151355 			    roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
5847a1306a7Sxc151355 
5857a1306a7Sxc151355 
5867a1306a7Sxc151355 		ATH_DEBUG((ATH_DBG_AUX, "ath: ath_beacon_config(): "
5877a1306a7Sxc151355 		    "intval %u nexttbtt %u dtim %u"
5887a1306a7Sxc151355 		    " nextdtim %u bmiss %u sleep %u\n",
5897a1306a7Sxc151355 		    bs.bs_intval,
5907a1306a7Sxc151355 		    bs.bs_nexttbtt,
5917a1306a7Sxc151355 		    bs.bs_dtimperiod,
5927a1306a7Sxc151355 		    bs.bs_nextdtim,
5937a1306a7Sxc151355 		    bs.bs_bmissthreshold,
5947a1306a7Sxc151355 		    bs.bs_sleepduration));
5957a1306a7Sxc151355 		ATH_HAL_INTRSET(ah, 0);
5967a1306a7Sxc151355 		/*
5977a1306a7Sxc151355 		 * Reset our tsf so the hardware will update the
5987a1306a7Sxc151355 		 * tsf register to reflect timestamps found in
5997a1306a7Sxc151355 		 * received beacons.
6007a1306a7Sxc151355 		 */
6017a1306a7Sxc151355 		ATH_HAL_RESETTSF(ah);
6027a1306a7Sxc151355 		ATH_HAL_BEACONTIMERS(ah, &bs);
6037a1306a7Sxc151355 		asc->asc_imask |= HAL_INT_BMISS;
6047a1306a7Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
6057a1306a7Sxc151355 	} else {
6067a1306a7Sxc151355 		ATH_HAL_INTRSET(ah, 0);
6077a1306a7Sxc151355 		ATH_HAL_BEACONINIT(ah, nexttbtt, in->in_intval);
6087a1306a7Sxc151355 		asc->asc_imask |= HAL_INT_SWBA;	/* beacon prepare */
6097a1306a7Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
6107a1306a7Sxc151355 	}
6117a1306a7Sxc151355 }
612*129d67acSlin wang - Sun Microsystems - Beijing China /*
613*129d67acSlin wang - Sun Microsystems - Beijing China  * Allocate tx/rx key slots for TKIP.  We allocate one slot for
614*129d67acSlin wang - Sun Microsystems - Beijing China  * each key. MIC is right after the decrypt/encrypt key.
615*129d67acSlin wang - Sun Microsystems - Beijing China  */
616*129d67acSlin wang - Sun Microsystems - Beijing China static uint16_t
617*129d67acSlin wang - Sun Microsystems - Beijing China key_alloc_pair(ath_t *asc, ieee80211_keyix *txkeyix,
618*129d67acSlin wang - Sun Microsystems - Beijing China     ieee80211_keyix *rxkeyix)
619*129d67acSlin wang - Sun Microsystems - Beijing China {
620*129d67acSlin wang - Sun Microsystems - Beijing China 	uint16_t i, keyix;
621*129d67acSlin wang - Sun Microsystems - Beijing China 
622*129d67acSlin wang - Sun Microsystems - Beijing China 	ASSERT(!asc->asc_splitmic);
623*129d67acSlin wang - Sun Microsystems - Beijing China 	for (i = 0; i < ATH_N(asc->asc_keymap)/4; i++) {
624*129d67acSlin wang - Sun Microsystems - Beijing China 		uint8_t b = asc->asc_keymap[i];
625*129d67acSlin wang - Sun Microsystems - Beijing China 		if (b == 0xff)
626*129d67acSlin wang - Sun Microsystems - Beijing China 			continue;
627*129d67acSlin wang - Sun Microsystems - Beijing China 		for (keyix = i * NBBY; keyix < (i + 1) * NBBY;
628*129d67acSlin wang - Sun Microsystems - Beijing China 		    keyix++, b >>= 1) {
629*129d67acSlin wang - Sun Microsystems - Beijing China 			if ((b & 1) || isset(asc->asc_keymap, keyix+64)) {
630*129d67acSlin wang - Sun Microsystems - Beijing China 				/* full pair unavailable */
631*129d67acSlin wang - Sun Microsystems - Beijing China 				continue;
632*129d67acSlin wang - Sun Microsystems - Beijing China 			}
633*129d67acSlin wang - Sun Microsystems - Beijing China 			setbit(asc->asc_keymap, keyix);
634*129d67acSlin wang - Sun Microsystems - Beijing China 			setbit(asc->asc_keymap, keyix+64);
635*129d67acSlin wang - Sun Microsystems - Beijing China 			ATH_DEBUG((ATH_DBG_AUX,
636*129d67acSlin wang - Sun Microsystems - Beijing China 			    "key_alloc_pair: key pair %u,%u\n",
637*129d67acSlin wang - Sun Microsystems - Beijing China 			    keyix, keyix+64));
638*129d67acSlin wang - Sun Microsystems - Beijing China 			*txkeyix = *rxkeyix = keyix;
639*129d67acSlin wang - Sun Microsystems - Beijing China 			return (1);
640*129d67acSlin wang - Sun Microsystems - Beijing China 		}
641*129d67acSlin wang - Sun Microsystems - Beijing China 	}
642*129d67acSlin wang - Sun Microsystems - Beijing China 	ATH_DEBUG((ATH_DBG_AUX, "key_alloc_pair:"
643*129d67acSlin wang - Sun Microsystems - Beijing China 	    " out of pair space\n"));
644*129d67acSlin wang - Sun Microsystems - Beijing China 	return (0);
645*129d67acSlin wang - Sun Microsystems - Beijing China }
6467a1306a7Sxc151355 
6470ba2cbe9Sxc151355 /*
648a399b765Szf162725  * Allocate tx/rx key slots for TKIP.  We allocate two slots for
649a399b765Szf162725  * each key, one for decrypt/encrypt and the other for the MIC.
650a399b765Szf162725  */
651a399b765Szf162725 static int
652a399b765Szf162725 key_alloc_2pair(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
653a399b765Szf162725 {
654a399b765Szf162725 	uint16_t i, keyix;
655a399b765Szf162725 
656a399b765Szf162725 	ASSERT(asc->asc_splitmic);
657a399b765Szf162725 	for (i = 0; i < ATH_N(asc->asc_keymap)/4; i++) {
658a399b765Szf162725 		uint8_t b = asc->asc_keymap[i];
659a399b765Szf162725 		if (b != 0xff) {
660a399b765Szf162725 			/*
661a399b765Szf162725 			 * One or more slots in this byte are free.
662a399b765Szf162725 			 */
663a399b765Szf162725 			keyix = i*NBBY;
664a399b765Szf162725 			while (b & 1) {
665a399b765Szf162725 		again:
666a399b765Szf162725 				keyix++;
667a399b765Szf162725 				b >>= 1;
668a399b765Szf162725 			}
669a399b765Szf162725 			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
670a399b765Szf162725 			if (isset(asc->asc_keymap, keyix+32) ||
671a399b765Szf162725 			    isset(asc->asc_keymap, keyix+64) ||
672a399b765Szf162725 			    isset(asc->asc_keymap, keyix+32+64)) {
673a399b765Szf162725 				/* full pair unavailable */
674a399b765Szf162725 				if (keyix == (i+1)*NBBY) {
675a399b765Szf162725 					/* no slots were appropriate, advance */
676a399b765Szf162725 					continue;
677a399b765Szf162725 				}
678a399b765Szf162725 				goto again;
679a399b765Szf162725 			}
680a399b765Szf162725 			setbit(asc->asc_keymap, keyix);
681a399b765Szf162725 			setbit(asc->asc_keymap, keyix+64);
682a399b765Szf162725 			setbit(asc->asc_keymap, keyix+32);
683a399b765Szf162725 			setbit(asc->asc_keymap, keyix+32+64);
684a399b765Szf162725 			ATH_DEBUG((ATH_DBG_AUX,
685a399b765Szf162725 			    "key_alloc_2pair: key pair %u,%u %u,%u\n",
686a399b765Szf162725 			    keyix, keyix+64,
687a399b765Szf162725 			    keyix+32, keyix+32+64));
688a399b765Szf162725 			*txkeyix = *rxkeyix = keyix;
689a399b765Szf162725 			return (1);
690a399b765Szf162725 		}
691a399b765Szf162725 	}
692a399b765Szf162725 	ATH_DEBUG((ATH_DBG_AUX, "key_alloc_2pair:"
693a399b765Szf162725 	    " out of pair space\n"));
694a399b765Szf162725 	return (0);
695a399b765Szf162725 }
696a399b765Szf162725 /*
697a399b765Szf162725  * Allocate a single key cache slot.
698a399b765Szf162725  */
699a399b765Szf162725 static int
700a399b765Szf162725 key_alloc_single(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
701a399b765Szf162725 {
702a399b765Szf162725 	uint16_t i, keyix;
703a399b765Szf162725 
704a399b765Szf162725 	/* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
705a399b765Szf162725 	for (i = 0; i < ATH_N(asc->asc_keymap); i++) {
706a399b765Szf162725 		uint8_t b = asc->asc_keymap[i];
707a399b765Szf162725 
708a399b765Szf162725 		if (b != 0xff) {
709a399b765Szf162725 			/*
710a399b765Szf162725 			 * One or more slots are free.
711a399b765Szf162725 			 */
712a399b765Szf162725 			keyix = i*NBBY;
713a399b765Szf162725 			while (b & 1)
714a399b765Szf162725 				keyix++, b >>= 1;
715a399b765Szf162725 			setbit(asc->asc_keymap, keyix);
716a399b765Szf162725 			ATH_DEBUG((ATH_DBG_AUX, "key_alloc_single:"
717a399b765Szf162725 			    " key %u\n", keyix));
718a399b765Szf162725 			*txkeyix = *rxkeyix = keyix;
719a399b765Szf162725 			return (1);
720a399b765Szf162725 		}
721a399b765Szf162725 	}
722a399b765Szf162725 	return (0);
723a399b765Szf162725 }
724a399b765Szf162725 
725a399b765Szf162725 /*
7260ba2cbe9Sxc151355  * Allocate one or more key cache slots for a unicast key.  The
7270ba2cbe9Sxc151355  * key itself is needed only to identify the cipher.  For hardware
7280ba2cbe9Sxc151355  * TKIP with split cipher+MIC keys we allocate two key cache slot
7290ba2cbe9Sxc151355  * pairs so that we can setup separate TX and RX MIC keys.  Note
7300ba2cbe9Sxc151355  * that the MIC key for a TKIP key at slot i is assumed by the
7310ba2cbe9Sxc151355  * hardware to be at slot i+64.  This limits TKIP keys to the first
7320ba2cbe9Sxc151355  * 64 entries.
7330ba2cbe9Sxc151355  */
7340ba2cbe9Sxc151355 /* ARGSUSED */
7350ba2cbe9Sxc151355 int
7360ba2cbe9Sxc151355 ath_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
7370ba2cbe9Sxc151355     ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
7380ba2cbe9Sxc151355 {
739a399b765Szf162725 	ath_t *asc = (ath_t *)ic;
740a399b765Szf162725 
741a399b765Szf162725 	/*
742a399b765Szf162725 	 * We allocate two pair for TKIP when using the h/w to do
743a399b765Szf162725 	 * the MIC.  For everything else, including software crypto,
744a399b765Szf162725 	 * we allocate a single entry.  Note that s/w crypto requires
745a399b765Szf162725 	 * a pass-through slot on the 5211 and 5212.  The 5210 does
746a399b765Szf162725 	 * not support pass-through cache entries and we map all
747a399b765Szf162725 	 * those requests to slot 0.
748a399b765Szf162725 	 */
749a399b765Szf162725 	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
750a399b765Szf162725 		return (key_alloc_single(asc, keyix, rxkeyix));
751a399b765Szf162725 	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
752*129d67acSlin wang - Sun Microsystems - Beijing China 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
753*129d67acSlin wang - Sun Microsystems - Beijing China 		if (asc->asc_splitmic)
754a399b765Szf162725 			return (key_alloc_2pair(asc, keyix, rxkeyix));
755*129d67acSlin wang - Sun Microsystems - Beijing China 		else
756*129d67acSlin wang - Sun Microsystems - Beijing China 			return (key_alloc_pair(asc, keyix, rxkeyix));
757a399b765Szf162725 	} else {
758a399b765Szf162725 		return (key_alloc_single(asc, keyix, rxkeyix));
759a399b765Szf162725 	}
7600ba2cbe9Sxc151355 }
7617a1306a7Sxc151355 
762a399b765Szf162725 /*
763a399b765Szf162725  * Delete an entry in the key cache allocated by ath_key_alloc.
764a399b765Szf162725  */
7650ba2cbe9Sxc151355 int
7660ba2cbe9Sxc151355 ath_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
7670ba2cbe9Sxc151355 {
768a399b765Szf162725 	ath_t *asc = (ath_t *)ic;
769a399b765Szf162725 	struct ath_hal *ah = asc->asc_ah;
770a399b765Szf162725 	const struct ieee80211_cipher *cip = k->wk_cipher;
771a399b765Szf162725 	ieee80211_keyix keyix = k->wk_keyix;
7720ba2cbe9Sxc151355 
773a399b765Szf162725 	ATH_DEBUG((ATH_DBG_AUX, "ath_key_delete:"
774a399b765Szf162725 	    " delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher));
775a399b765Szf162725 
776a399b765Szf162725 	ATH_HAL_KEYRESET(ah, keyix);
777a399b765Szf162725 	/*
778a399b765Szf162725 	 * Handle split tx/rx keying required for TKIP with h/w MIC.
779a399b765Szf162725 	 */
780a399b765Szf162725 	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
781a399b765Szf162725 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && asc->asc_splitmic)
782a399b765Szf162725 		ATH_HAL_KEYRESET(ah, keyix+32);		/* RX key */
783a399b765Szf162725 
784a399b765Szf162725 	if (keyix >= IEEE80211_WEP_NKID) {
785a399b765Szf162725 		/*
786a399b765Szf162725 		 * Don't touch keymap entries for global keys so
787a399b765Szf162725 		 * they are never considered for dynamic allocation.
788a399b765Szf162725 		 */
789a399b765Szf162725 		clrbit(asc->asc_keymap, keyix);
790a399b765Szf162725 		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
791*129d67acSlin wang - Sun Microsystems - Beijing China 		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
792*129d67acSlin wang - Sun Microsystems - Beijing China 			/*
793*129d67acSlin wang - Sun Microsystems - Beijing China 			 * If splitmic is true +64 is TX key MIC,
794*129d67acSlin wang - Sun Microsystems - Beijing China 			 * else +64 is RX key + RX key MIC.
795*129d67acSlin wang - Sun Microsystems - Beijing China 			 */
796*129d67acSlin wang - Sun Microsystems - Beijing China 			clrbit(asc->asc_keymap, keyix+64);
797*129d67acSlin wang - Sun Microsystems - Beijing China 			if (asc->asc_splitmic) {
798*129d67acSlin wang - Sun Microsystems - Beijing China 				/* Rx key */
799*129d67acSlin wang - Sun Microsystems - Beijing China 				clrbit(asc->asc_keymap, keyix+32);
800*129d67acSlin wang - Sun Microsystems - Beijing China 				/* RX key MIC */
801*129d67acSlin wang - Sun Microsystems - Beijing China 				clrbit(asc->asc_keymap, keyix+32+64);
802*129d67acSlin wang - Sun Microsystems - Beijing China 			}
803a399b765Szf162725 		}
804a399b765Szf162725 	}
8050ba2cbe9Sxc151355 	return (1);
8060ba2cbe9Sxc151355 }
8077a1306a7Sxc151355 
808a399b765Szf162725 static void
809*129d67acSlin wang - Sun Microsystems - Beijing China ath_keyprint(ath_t *asc, const char *tag, uint_t ix,
810a399b765Szf162725     const HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
811a399b765Szf162725 {
812a399b765Szf162725 	static const char *ciphers[] = {
813a399b765Szf162725 		"WEP",
814a399b765Szf162725 		"AES-OCB",
815a399b765Szf162725 		"AES-CCM",
816a399b765Szf162725 		"CKIP",
817a399b765Szf162725 		"TKIP",
818a399b765Szf162725 		"CLR",
819a399b765Szf162725 	};
820a399b765Szf162725 	int i, n;
821a399b765Szf162725 	char buf[MAX_IEEE80211STR], buft[32];
822a399b765Szf162725 
823a399b765Szf162725 	(void) snprintf(buf, sizeof (buf), "%s: [%02u] %s ",
824a399b765Szf162725 	    tag, ix, ciphers[hk->kv_type]);
825a399b765Szf162725 	for (i = 0, n = hk->kv_len; i < n; i++) {
826a399b765Szf162725 		(void) snprintf(buft, sizeof (buft), "%02x", hk->kv_val[i]);
827a399b765Szf162725 		(void) strlcat(buf, buft, sizeof (buf));
828a399b765Szf162725 	}
829a399b765Szf162725 	(void) snprintf(buft, sizeof (buft), " mac %s",
830a399b765Szf162725 	    ieee80211_macaddr_sprintf(mac));
831a399b765Szf162725 	(void) strlcat(buf, buft, sizeof (buf));
832a399b765Szf162725 	if (hk->kv_type == HAL_CIPHER_TKIP) {
833*129d67acSlin wang - Sun Microsystems - Beijing China 		(void) snprintf(buft, sizeof (buft), " %s ",
834*129d67acSlin wang - Sun Microsystems - Beijing China 		    asc->asc_splitmic ? "mic" : "rxmic");
835a399b765Szf162725 		(void) strlcat(buf, buft, sizeof (buf));
836a399b765Szf162725 		for (i = 0; i < sizeof (hk->kv_mic); i++) {
837a399b765Szf162725 			(void) snprintf(buft, sizeof (buft), "%02x",
838a399b765Szf162725 			    hk->kv_mic[i]);
839a399b765Szf162725 			(void) strlcat(buf, buft, sizeof (buf));
840a399b765Szf162725 		}
841*129d67acSlin wang - Sun Microsystems - Beijing China 		if (!asc->asc_splitmic) {
842*129d67acSlin wang - Sun Microsystems - Beijing China 			(void) snprintf(buft, sizeof (buft), " txmic ");
843*129d67acSlin wang - Sun Microsystems - Beijing China 			(void) strlcat(buf, buft, sizeof (buf));
844*129d67acSlin wang - Sun Microsystems - Beijing China 			for (i = 0; i < sizeof (hk->kv_txmic); i++) {
845*129d67acSlin wang - Sun Microsystems - Beijing China 				(void) snprintf(buft, sizeof (buft), "%02x",
846*129d67acSlin wang - Sun Microsystems - Beijing China 				    hk->kv_txmic[i]);
847*129d67acSlin wang - Sun Microsystems - Beijing China 				(void) strlcat(buf, buft, sizeof (buf));
848*129d67acSlin wang - Sun Microsystems - Beijing China 			}
849*129d67acSlin wang - Sun Microsystems - Beijing China 		}
850a399b765Szf162725 	}
851a399b765Szf162725 	ATH_DEBUG((ATH_DBG_AUX, "%s", buf));
852a399b765Szf162725 }
853a399b765Szf162725 
854a399b765Szf162725 /*
855a399b765Szf162725  * Set a TKIP key into the hardware.  This handles the
856a399b765Szf162725  * potential distribution of key state to multiple key
857a399b765Szf162725  * cache slots for TKIP.
858a399b765Szf162725  */
859a399b765Szf162725 static int
860a399b765Szf162725 ath_keyset_tkip(ath_t *asc, const struct ieee80211_key *k,
861a399b765Szf162725 	HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
862a399b765Szf162725 {
863a399b765Szf162725 #define	IEEE80211_KEY_XR	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
864a399b765Szf162725 	static const uint8_t zerobssid[IEEE80211_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
865a399b765Szf162725 	struct ath_hal *ah = asc->asc_ah;
866a399b765Szf162725 
867a399b765Szf162725 	ASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP);
868a399b765Szf162725 	if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
869*129d67acSlin wang - Sun Microsystems - Beijing China 		if (asc->asc_splitmic) {
870a399b765Szf162725 			/*
871a399b765Szf162725 			 * TX key goes at first index, RX key at +32.
872a399b765Szf162725 			 * The hal handles the MIC keys at index+64.
873a399b765Szf162725 			 */
874*129d67acSlin wang - Sun Microsystems - Beijing China 			(void) memcpy(hk->kv_mic, k->wk_txmic,
875*129d67acSlin wang - Sun Microsystems - Beijing China 			    sizeof (hk->kv_mic));
876*129d67acSlin wang - Sun Microsystems - Beijing China 			ath_keyprint(asc, "ath_keyset_tkip:", k->wk_keyix, hk,
877*129d67acSlin wang - Sun Microsystems - Beijing China 			    zerobssid);
878a399b765Szf162725 			if (!ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid))
879a399b765Szf162725 				return (0);
880a399b765Szf162725 
881*129d67acSlin wang - Sun Microsystems - Beijing China 			(void) memcpy(hk->kv_mic, k->wk_rxmic,
882*129d67acSlin wang - Sun Microsystems - Beijing China 			    sizeof (hk->kv_mic));
883*129d67acSlin wang - Sun Microsystems - Beijing China 			ath_keyprint(asc, "ath_keyset_tkip:", k->wk_keyix+32,
884*129d67acSlin wang - Sun Microsystems - Beijing China 			    hk, mac);
885a399b765Szf162725 			return (ATH_HAL_KEYSET(ah, k->wk_keyix+32, hk, mac));
886*129d67acSlin wang - Sun Microsystems - Beijing China 		} else {
887*129d67acSlin wang - Sun Microsystems - Beijing China 			/*
888*129d67acSlin wang - Sun Microsystems - Beijing China 			 * Room for both TX+RX MIC keys in one key cache
889*129d67acSlin wang - Sun Microsystems - Beijing China 			 * slot, just set key at the first index; the hal
890*129d67acSlin wang - Sun Microsystems - Beijing China 			 * will handle the reset.
891*129d67acSlin wang - Sun Microsystems - Beijing China 			 */
892*129d67acSlin wang - Sun Microsystems - Beijing China 			(void) memcpy(hk->kv_mic, k->wk_rxmic,
893*129d67acSlin wang - Sun Microsystems - Beijing China 			    sizeof (hk->kv_mic));
894*129d67acSlin wang - Sun Microsystems - Beijing China 			(void) memcpy(hk->kv_txmic, k->wk_txmic,
895*129d67acSlin wang - Sun Microsystems - Beijing China 			    sizeof (hk->kv_txmic));
896*129d67acSlin wang - Sun Microsystems - Beijing China 			ath_keyprint(asc, "ath_keyset_tkip", k->wk_keyix, hk,
897*129d67acSlin wang - Sun Microsystems - Beijing China 			    mac);
898*129d67acSlin wang - Sun Microsystems - Beijing China 			return (ATH_HAL_KEYSET(ah, k->wk_keyix, hk, mac));
899*129d67acSlin wang - Sun Microsystems - Beijing China 		}
900a399b765Szf162725 	} else if (k->wk_flags & IEEE80211_KEY_XR) {
901a399b765Szf162725 		/*
902a399b765Szf162725 		 * TX/RX key goes at first index.
903a399b765Szf162725 		 * The hal handles the MIC keys are index+64.
904a399b765Szf162725 		 */
905a399b765Szf162725 		(void) memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
906a399b765Szf162725 		    k->wk_txmic : k->wk_rxmic, sizeof (hk->kv_mic));
907*129d67acSlin wang - Sun Microsystems - Beijing China 		ath_keyprint(asc, "ath_keyset_tkip:", k->wk_keyix, hk,
908*129d67acSlin wang - Sun Microsystems - Beijing China 		    zerobssid);
909a399b765Szf162725 		return (ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid));
910a399b765Szf162725 	}
911a399b765Szf162725 	return (0);
912a399b765Szf162725 #undef IEEE80211_KEY_XR
913a399b765Szf162725 }
914a399b765Szf162725 
9157a1306a7Sxc151355 /*
9160ba2cbe9Sxc151355  * Set the key cache contents for the specified key.  Key cache
9170ba2cbe9Sxc151355  * slot(s) must already have been allocated by ath_key_alloc.
9187a1306a7Sxc151355  */
9190ba2cbe9Sxc151355 int
9200ba2cbe9Sxc151355 ath_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
9210ba2cbe9Sxc151355     const uint8_t mac[IEEE80211_ADDR_LEN])
9227a1306a7Sxc151355 {
9230ba2cbe9Sxc151355 	static const uint8_t ciphermap[] = {
9240ba2cbe9Sxc151355 		HAL_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
9250ba2cbe9Sxc151355 		HAL_CIPHER_TKIP,	/* IEEE80211_CIPHER_TKIP */
9260ba2cbe9Sxc151355 		HAL_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
9270ba2cbe9Sxc151355 		HAL_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
9280ba2cbe9Sxc151355 		HAL_CIPHER_CKIP,	/* IEEE80211_CIPHER_CKIP */
9290ba2cbe9Sxc151355 		HAL_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
9300ba2cbe9Sxc151355 	};
9310ba2cbe9Sxc151355 	ath_t *asc = (ath_t *)ic;
9327a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
9330ba2cbe9Sxc151355 	const struct ieee80211_cipher *cip = k->wk_cipher;
9347a1306a7Sxc151355 	HAL_KEYVAL hk;
9357a1306a7Sxc151355 
9367a1306a7Sxc151355 	bzero(&hk, sizeof (hk));
9370ba2cbe9Sxc151355 	/*
9380ba2cbe9Sxc151355 	 * Software crypto uses a "clear key" so non-crypto
9390ba2cbe9Sxc151355 	 * state kept in the key cache are maintainedd so that
9400ba2cbe9Sxc151355 	 * rx frames have an entry to match.
9410ba2cbe9Sxc151355 	 */
9420ba2cbe9Sxc151355 	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
9430ba2cbe9Sxc151355 		ASSERT(cip->ic_cipher < ATH_N(ciphermap));
9440ba2cbe9Sxc151355 		hk.kv_type = ciphermap[cip->ic_cipher];
9450ba2cbe9Sxc151355 		hk.kv_len = k->wk_keylen;
9460ba2cbe9Sxc151355 		bcopy(k->wk_key, hk.kv_val, k->wk_keylen);
9470ba2cbe9Sxc151355 	} else {
9480ba2cbe9Sxc151355 		hk.kv_type = HAL_CIPHER_CLR;
9497a1306a7Sxc151355 	}
9507a1306a7Sxc151355 
951a399b765Szf162725 	if (hk.kv_type == HAL_CIPHER_TKIP &&
952*129d67acSlin wang - Sun Microsystems - Beijing China 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
953a399b765Szf162725 		return (ath_keyset_tkip(asc, k, &hk, mac));
954a399b765Szf162725 	} else {
955*129d67acSlin wang - Sun Microsystems - Beijing China 		ath_keyprint(asc, "ath_keyset:", k->wk_keyix, &hk, mac);
9560ba2cbe9Sxc151355 		return (ATH_HAL_KEYSET(ah, k->wk_keyix, &hk, mac));
9570ba2cbe9Sxc151355 	}
958a399b765Szf162725 }
9590ba2cbe9Sxc151355 
9600ba2cbe9Sxc151355 /*
9610ba2cbe9Sxc151355  * Enable/Disable short slot timing
9620ba2cbe9Sxc151355  */
9637a1306a7Sxc151355 void
9640ba2cbe9Sxc151355 ath_set_shortslot(ieee80211com_t *ic, int onoff)
9657a1306a7Sxc151355 {
9660ba2cbe9Sxc151355 	struct ath_hal *ah = ((ath_t *)ic)->asc_ah;
9670ba2cbe9Sxc151355 
9680ba2cbe9Sxc151355 	if (onoff)
9690ba2cbe9Sxc151355 		ATH_HAL_SETSLOTTIME(ah, HAL_SLOT_TIME_9);
9700ba2cbe9Sxc151355 	else
9710ba2cbe9Sxc151355 		ATH_HAL_SETSLOTTIME(ah, HAL_SLOT_TIME_20);
9720ba2cbe9Sxc151355 }
9730ba2cbe9Sxc151355 
9740ba2cbe9Sxc151355 int
9750ba2cbe9Sxc151355 ath_reset(ieee80211com_t *ic)
9760ba2cbe9Sxc151355 {
9770ba2cbe9Sxc151355 	ath_t *asc = (ath_t *)ic;
9787a1306a7Sxc151355 	struct ath_hal *ah = asc->asc_ah;
9790ba2cbe9Sxc151355 	struct ieee80211_channel *ch;
9807a1306a7Sxc151355 	HAL_STATUS status;
9817a1306a7Sxc151355 
9827a1306a7Sxc151355 	/*
9837a1306a7Sxc151355 	 * Convert to a HAL channel description with the flags
9847a1306a7Sxc151355 	 * constrained to reflect the current operating mode.
9857a1306a7Sxc151355 	 */
9860ba2cbe9Sxc151355 	ch = ic->ic_curchan;
9870ba2cbe9Sxc151355 	asc->asc_curchan.channel = ch->ich_freq;
9880ba2cbe9Sxc151355 	asc->asc_curchan.channelFlags = ath_chan2flags(ic, ch);
9897a1306a7Sxc151355 
9907a1306a7Sxc151355 	ATH_HAL_INTRSET(ah, 0);		/* disable interrupts */
9917a1306a7Sxc151355 	ath_draintxq(asc);		/* stop xmit side */
9920ba2cbe9Sxc151355 	if (ATH_IS_RUNNING(asc)) {
9937a1306a7Sxc151355 		ath_stoprecv(asc);		/* stop recv side */
9947a1306a7Sxc151355 		/* indicate channel change so we do a full reset */
9953caf1114Sxc151355 		if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
9963caf1114Sxc151355 		    &asc->asc_curchan, AH_TRUE, &status)) {
9977a1306a7Sxc151355 			ath_problem("ath: ath_reset(): "
9983caf1114Sxc151355 			    "resetting hardware failed, '%s' (HAL status %u)\n",
9993caf1114Sxc151355 			    ath_get_hal_status_desc(status), status);
10007a1306a7Sxc151355 		}
10010ba2cbe9Sxc151355 		ath_chan_change(asc, ch);
10020ba2cbe9Sxc151355 	}
10030ba2cbe9Sxc151355 	if (ATH_IS_RUNNING(asc)) {
10047a1306a7Sxc151355 		if (ath_startrecv(asc) != 0)	/* restart recv */
10057a1306a7Sxc151355 			ath_problem("ath: ath_reset(): "
10067a1306a7Sxc151355 			    "starting receiving logic failed\n");
10070ba2cbe9Sxc151355 		if (ic->ic_state == IEEE80211_S_RUN) {
10087a1306a7Sxc151355 			ath_beacon_config(asc);	/* restart beacons */
10097a1306a7Sxc151355 		}
10107a1306a7Sxc151355 		ATH_HAL_INTRSET(ah, asc->asc_imask);
10117a1306a7Sxc151355 	}
10120ba2cbe9Sxc151355 	return (0);
10137a1306a7Sxc151355 }
1014