summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/ath/ath_aux.c
diff options
context:
space:
mode:
authorxc151355 <none@none>2005-11-27 19:11:23 -0800
committerxc151355 <none@none>2005-11-27 19:11:23 -0800
commit7a1306a70fee0e017a445bde1dcfd1997f691cf4 (patch)
tree383e9f41f9a495e95fa1376b4b36b3fba7ea3620 /usr/src/uts/common/io/ath/ath_aux.c
parente23a7e348c32cb3a1a7072dcac6905a150a028d7 (diff)
downloadillumos-gate-7a1306a70fee0e017a445bde1dcfd1997f691cf4.tar.gz
PSARC 2005/440 Atheros 802.11b/g driver
5050300 Solaris need Atheros AR5212 WiFi driver
Diffstat (limited to 'usr/src/uts/common/io/ath/ath_aux.c')
-rw-r--r--usr/src/uts/common/io/ath/ath_aux.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/ath/ath_aux.c b/usr/src/uts/common/io/ath/ath_aux.c
new file mode 100644
index 0000000000..fb119a22af
--- /dev/null
+++ b/usr/src/uts/common/io/ath/ath_aux.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/stream.h>
+#include <sys/termio.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/cmn_err.h>
+#include <sys/stropts.h>
+#include <sys/strsubr.h>
+#include <sys/strtty.h>
+#include <sys/kbio.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/consdev.h>
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/pci.h>
+#include <sys/errno.h>
+#include <sys/gld.h>
+#include <sys/dlpi.h>
+#include <sys/ethernet.h>
+#include <sys/list.h>
+#include <sys/byteorder.h>
+#include <sys/strsun.h>
+#include <inet/common.h>
+#include <inet/nd.h>
+#include <inet/mi.h>
+#include <inet/wifi_ioctl.h>
+#include "ath_hal.h"
+#include "ath_impl.h"
+#include "ath_ieee80211.h"
+
+static const char *acnames[] = {
+ "WME_AC_BE",
+ "WME_AC_BK",
+ "WME_AC_VI",
+ "WME_AC_VO",
+ "WME_UPSD"
+};
+
+extern void ath_setup_desc(ath_t *asc, struct ath_buf *bf);
+
+uint32_t
+ath_calcrxfilter(ath_t *asc)
+{
+ ieee80211com_t *isc = (ieee80211com_t *)asc;
+ struct ath_hal *ah = asc->asc_ah;
+ uint32_t rfilt;
+
+ rfilt = (ATH_HAL_GETRXFILTER(ah) & HAL_RX_FILTER_PHYERR)
+ | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+ if (isc->isc_opmode != IEEE80211_M_STA)
+ rfilt |= HAL_RX_FILTER_PROBEREQ;
+ if (isc->isc_opmode != IEEE80211_M_HOSTAP &&
+ (asc->asc_promisc & GLD_MAC_PROMISC_PHYS)) /* promiscuous */
+ rfilt |= HAL_RX_FILTER_PROM;
+ if (isc->isc_opmode == IEEE80211_M_STA ||
+ isc->isc_opmode == IEEE80211_M_IBSS ||
+ isc->isc_state == IEEE80211_S_SCAN)
+ rfilt |= HAL_RX_FILTER_BEACON;
+ return (rfilt);
+}
+
+static int
+ath_set_data_queue(ath_t *asc, int ac, int haltype)
+{
+ HAL_TXQ_INFO qi;
+ int qnum;
+ struct ath_hal *ah = asc->asc_ah;
+ struct ath_txq *txq;
+
+ if (ac >= ATH_N(asc->asc_ac2q)) {
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
+ "ac %u out of range, max %u!\n",
+ ac, ATH_N(asc->asc_ac2q)));
+ return (1);
+ }
+ (void) memset(&qi, 0, sizeof (qi));
+ qi.tqi_subtype = haltype;
+ /*
+ * Enable interrupts only for EOL and DESC conditions.
+ * We mark tx descriptors to receive a DESC interrupt
+ * when a tx queue gets deep; otherwise waiting for the
+ * EOL to reap descriptors. Note that this is done to
+ * reduce interrupt load and this only defers reaping
+ * descriptors, never transmitting frames. Aside from
+ * reducing interrupts this also permits more concurrency.
+ * The only potential downside is if the tx queue backs
+ * up in which case the top half of the kernel may backup
+ * due to a lack of tx descriptors.
+ */
+ qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
+ qnum = ATH_HAL_SETUPTXQUEUE(ah, HAL_TX_QUEUE_DATA, &qi);
+ if (qnum == -1) {
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
+ "Unable to setup hardware queue for %s traffic!\n",
+ acnames[ac]));
+ return (1);
+ }
+ if (qnum >= ATH_N(asc->asc_txq)) {
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_set_data_queue(): "
+ "hal qnum %u out of range, max %u!\n",
+ qnum, ATH_N(asc->asc_txq)));
+ return (1);
+ }
+ if (!ATH_TXQ_SETUP(asc, qnum)) {
+ txq = &asc->asc_txq[qnum];
+ txq->axq_qnum = qnum;
+ txq->axq_depth = 0;
+ txq->axq_intrcnt = 0;
+ txq->axq_link = NULL;
+ list_create(&txq->axq_list, sizeof (struct ath_buf),
+ offsetof(struct ath_buf, bf_node));
+ mutex_init(&txq->axq_lock, NULL, MUTEX_DRIVER, NULL);
+ asc->asc_txqsetup |= 1<<qnum;
+ }
+ asc->asc_ac2q[ac] = &asc->asc_txq[qnum];
+ return (0);
+}
+
+int
+ath_txq_setup(ath_t *asc)
+{
+ if (ath_set_data_queue(asc, WME_AC_BE, HAL_WME_AC_BK) ||
+ ath_set_data_queue(asc, WME_AC_BK, HAL_WME_AC_BE) ||
+ ath_set_data_queue(asc, WME_AC_VI, HAL_WME_AC_VI) ||
+ ath_set_data_queue(asc, WME_AC_VO, HAL_WME_AC_VO)) {
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+ath_setcurmode(ath_t *asc, enum ieee80211_phymode mode)
+{
+ const HAL_RATE_TABLE *rt;
+ int i;
+
+ for (i = 0; i < sizeof (asc->asc_rixmap); i++)
+ asc->asc_rixmap[i] = 0xff;
+
+ rt = asc->asc_rates[mode];
+ ASSERT(rt != NULL);
+
+ for (i = 0; i < rt->rateCount; i++)
+ asc->asc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
+
+ asc->asc_currates = rt;
+ asc->asc_curmode = mode;
+}
+
+/* Set correct parameters for a certain mode */
+void
+ath_mode_init(ath_t *asc)
+{
+ ieee80211com_t *isc = (ieee80211com_t *)asc;
+ struct ath_hal *ah = asc->asc_ah;
+ uint32_t rfilt;
+
+ /* configure rx filter */
+ rfilt = ath_calcrxfilter(asc);
+ ATH_HAL_SETRXFILTER(ah, rfilt);
+ ATH_HAL_SETOPMODE(ah);
+ ATH_HAL_SETMCASTFILTER(ah, asc->asc_mfilt[0], asc->asc_mfilt[1]);
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_mode_init(): "
+ "mode =%d RX filter 0x%x, MC filter %08x:%08x\n",
+ isc->isc_opmode, rfilt,
+ asc->asc_mfilt[0], asc->asc_mfilt[1]));
+}
+
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+void
+ath_stoprecv(ath_t *asc)
+{
+ ATH_HAL_STOPPCURECV(asc->asc_ah); /* disable PCU */
+ ATH_HAL_SETRXFILTER(asc->asc_ah, 0); /* clear recv filter */
+ ATH_HAL_STOPDMARECV(asc->asc_ah); /* disable DMA engine */
+ drv_usecwait(3000);
+
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_stoprecv(): rx queue %p, link %p\n",
+ ATH_HAL_GETRXBUF(asc->asc_ah), asc->asc_rxlink));
+ asc->asc_rxlink = NULL;
+}
+
+uint32_t
+ath_chan2flags(ieee80211com_t *isc, struct ieee80211channel *chan)
+{
+ static const uint32_t modeflags[] = {
+ 0, /* IEEE80211_MODE_AUTO */
+ CHANNEL_A, /* IEEE80211_MODE_11A */
+ CHANNEL_B, /* IEEE80211_MODE_11B */
+ CHANNEL_PUREG, /* IEEE80211_MODE_11G */
+ CHANNEL_T /* IEEE80211_MODE_TURBO */
+ };
+ return (modeflags[ieee80211_chan2mode(isc, chan)]);
+}
+
+
+int
+ath_getchannels(ath_t *asc, uint32_t cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
+{
+ ieee80211com_t *isc = (ieee80211com_t *)asc;
+ struct ath_hal *ah = asc->asc_ah;
+ HAL_CHANNEL *chans;
+ int i, ix;
+ uint32_t nchan;
+
+ chans = (HAL_CHANNEL *)
+ kmem_zalloc(IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL), KM_SLEEP);
+
+ if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
+ cc, HAL_MODE_ALL, outdoor, xchanmode)) {
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
+ "unable to get channel list\n");
+ kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL)));
+ return (EINVAL);
+ }
+
+ /*
+ * Convert HAL channels to ieee80211 ones and insert
+ * them in the table according to their channel number.
+ */
+ for (i = 0; i < nchan; i++) {
+ HAL_CHANNEL *c = &chans[i];
+ ix = ath_hal_mhz2ieee(c->channel, c->channelFlags);
+ if (ix > IEEE80211_CHAN_MAX) {
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_getchannels(): "
+ "bad hal channel %u (%u/%x) ignored\n",
+ ix, c->channel, c->channelFlags));
+ continue;
+ }
+ /* NB: flags are known to be compatible */
+ if (isc->isc_channels[ix].ich_freq == 0) {
+ isc->isc_channels[ix].ich_freq = c->channel;
+ isc->isc_channels[ix].ich_flags = c->channelFlags;
+ } else {
+ /* channels overlap; e.g. 11g and 11b */
+ isc->isc_channels[ix].ich_flags |= c->channelFlags;
+ }
+ if ((c->channelFlags & CHANNEL_G) == CHANNEL_G)
+ asc->asc_have11g = 1;
+ }
+ kmem_free(chans, IEEE80211_CHAN_MAX * sizeof (HAL_CHANNEL));
+ return (0);
+}
+
+static void
+ath_drainq(ath_t *asc, struct ath_txq *txq)
+{
+ struct ath_buf *bf;
+
+ /*
+ * This assumes output has been stopped.
+ */
+ for (;;) {
+ mutex_enter(&txq->axq_lock);
+ bf = list_head(&txq->axq_list);
+ if (bf == NULL) {
+ txq->axq_link = NULL;
+ mutex_exit(&txq->axq_lock);
+ break;
+ }
+ list_remove(&txq->axq_list, bf);
+ mutex_exit(&txq->axq_lock);
+ bf->bf_in = NULL;
+ mutex_enter(&asc->asc_txbuflock);
+ list_insert_tail(&asc->asc_txbuf_list, bf);
+ mutex_exit(&asc->asc_txbuflock);
+ }
+}
+
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+void
+ath_draintxq(ath_t *asc)
+{
+ struct ath_hal *ah = asc->asc_ah;
+ struct ath_txq *txq;
+ int i;
+
+ if (!asc->asc_invalid) {
+ for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+ if (ATH_TXQ_SETUP(asc, i)) {
+ txq = &asc->asc_txq[i];
+ (void) ATH_HAL_STOPTXDMA(ah, txq->axq_qnum);
+ }
+ }
+ }
+ for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+ if (ATH_TXQ_SETUP(asc, i)) {
+ ath_drainq(asc, &asc->asc_txq[i]);
+ }
+ }
+}
+
+
+/* Enable the receive h/w following a reset */
+int
+ath_startrecv(ath_t *asc)
+{
+ struct ath_buf *bf;
+
+ asc->asc_rxlink = NULL;
+
+ bf = list_head(&asc->asc_rxbuf_list);
+ while (bf != NULL) {
+ ath_setup_desc(asc, bf);
+ bf = list_next(&asc->asc_rxbuf_list, bf);
+ }
+
+ bf = list_head(&asc->asc_rxbuf_list);
+ ATH_HAL_PUTRXBUF(asc->asc_ah, bf->bf_daddr);
+ ATH_HAL_RXENA(asc->asc_ah); /* enable recv descriptors */
+ ath_mode_init(asc); /* set filters, etc. */
+ ATH_HAL_STARTPCURECV(asc->asc_ah); /* re-enable PCU/DMA engine */
+ return (0);
+}
+
+/*
+ * Set/change channels. If the channel is really being changed,
+ * it's done by resetting the chip. To accomplish this we must
+ * first cleanup any pending DMA.
+ */
+int
+ath_chan_set(ath_t *asc, struct ieee80211channel *chan)
+{
+ struct ath_hal *ah = asc->asc_ah;
+ ieee80211com_t *isc = &asc->asc_isc;
+
+ if (chan != isc->isc_ibss_chan) {
+ HAL_STATUS status;
+ HAL_CHANNEL hchan;
+ enum ieee80211_phymode mode;
+
+ /*
+ * To switch channels clear any pending DMA operations;
+ * wait long enough for the RX fifo to drain, reset the
+ * hardware at the new frequency, and then re-enable
+ * the relevant bits of the h/w.
+ */
+ ATH_HAL_INTRSET(ah, 0); /* disable interrupts */
+ ath_draintxq(asc); /* clear pending tx frames */
+ ath_stoprecv(asc); /* turn off frame recv */
+ /*
+ * Convert to a HAL channel description with
+ * the flags constrained to reflect the current
+ * operating mode.
+ */
+ hchan.channel = chan->ich_freq;
+ hchan.channelFlags = ath_chan2flags(isc, chan);
+ if (!ATH_HAL_RESET(ah, (HAL_OPMODE)isc->isc_opmode,
+ &hchan, AH_TRUE, &status)) {
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_chan_set():"
+ "unable to reset channel %u (%uMhz)\n",
+ ieee80211_chan2ieee(isc, chan), chan->ich_freq));
+ return (EIO);
+ }
+
+ /*
+ * Re-enable rx framework.
+ */
+ if (ath_startrecv(asc) != 0) {
+ ath_problem("ath: ath_chan_set(): "
+ "restarting receiving logic failed\n");
+ return (EIO);
+ }
+
+ /*
+ * Change channels and update the h/w rate map
+ * if we're switching; e.g. 11a to 11b/g.
+ */
+ isc->isc_ibss_chan = chan;
+ mode = ieee80211_chan2mode(isc, chan);
+ if (mode != asc->asc_curmode)
+ ath_setcurmode(asc, mode);
+ /*
+ * Re-enable interrupts.
+ */
+ ATH_HAL_INTRSET(ah, asc->asc_imask);
+ }
+ return (0);
+}
+
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wakeup in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+void
+ath_beacon_config(ath_t *asc)
+{
+ struct ath_hal *ah = asc->asc_ah;
+ ieee80211com_t *isc = (ieee80211com_t *)asc;
+ struct ieee80211_node *in = isc->isc_bss;
+ uint32_t nexttbtt;
+
+ nexttbtt = (ATH_LE_READ_4(in->in_tstamp + 4) << 22) |
+ (ATH_LE_READ_4(in->in_tstamp) >> 10);
+ nexttbtt += in->in_intval;
+ if (isc->isc_opmode != IEEE80211_M_HOSTAP) {
+ HAL_BEACON_STATE bs;
+ uint32_t bmisstime;
+
+ /* NB: no PCF support right now */
+ bzero(&bs, sizeof (bs));
+ bs.bs_intval = in->in_intval;
+ bs.bs_nexttbtt = nexttbtt;
+ bs.bs_dtimperiod = bs.bs_intval;
+ bs.bs_nextdtim = nexttbtt;
+
+ /*
+ * Calculate the number of consecutive beacons to miss
+ * before taking a BMISS interrupt. The configuration
+ * is specified in ms, so we need to convert that to
+ * TU's and then calculate based on the beacon interval.
+ * Note that we clamp the result to at most 10 beacons.
+ */
+ bmisstime = (isc->isc_bmisstimeout * 1000) / 1024;
+ bs.bs_bmissthreshold = howmany(bmisstime, in->in_intval);
+ if (bs.bs_bmissthreshold > 10)
+ bs.bs_bmissthreshold = 10;
+ else if (bs.bs_bmissthreshold <= 0)
+ bs.bs_bmissthreshold = 1;
+ /*
+ * Calculate sleep duration. The configuration is
+ * given in ms. We insure a multiple of the beacon
+ * period is used. Also, if the sleep duration is
+ * greater than the DTIM period then it makes senses
+ * to make it a multiple of that.
+ */
+ bs.bs_sleepduration =
+ roundup((100 * 1000) / 1024, bs.bs_intval);
+ if (bs.bs_sleepduration > bs.bs_dtimperiod)
+ bs.bs_sleepduration =
+ roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
+
+
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_beacon_config(): "
+ "intval %u nexttbtt %u dtim %u"
+ " nextdtim %u bmiss %u sleep %u\n",
+ bs.bs_intval,
+ bs.bs_nexttbtt,
+ bs.bs_dtimperiod,
+ bs.bs_nextdtim,
+ bs.bs_bmissthreshold,
+ bs.bs_sleepduration));
+ ATH_HAL_INTRSET(ah, 0);
+ /*
+ * Reset our tsf so the hardware will update the
+ * tsf register to reflect timestamps found in
+ * received beacons.
+ */
+ ATH_HAL_RESETTSF(ah);
+ ATH_HAL_BEACONTIMERS(ah, &bs);
+ asc->asc_imask |= HAL_INT_BMISS;
+ ATH_HAL_INTRSET(ah, asc->asc_imask);
+ } else {
+ ATH_HAL_INTRSET(ah, 0);
+ ATH_HAL_BEACONINIT(ah, nexttbtt, in->in_intval);
+ asc->asc_imask |= HAL_INT_SWBA; /* beacon prepare */
+ ATH_HAL_INTRSET(ah, asc->asc_imask);
+ }
+}
+
+
+
+/*
+ * Fill the hardware key cache with key entries.
+ */
+void
+ath_initkeytable(ath_t *asc)
+{
+ ieee80211com_t *isc = (ieee80211com_t *)asc;
+ struct ath_hal *ah = asc->asc_ah;
+ int32_t i;
+
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ struct ieee80211_wepkey *k = &isc->isc_nw_keys[i];
+ if (k->iwk_len == 0)
+ ATH_HAL_KEYRESET(ah, i);
+ else {
+ HAL_KEYVAL hk;
+
+#ifdef DEBUG
+ char tmp[200], stmp[10];
+ int j;
+ bzero(tmp, 200);
+ bzero(stmp, 10);
+ for (j = 0; j < k->iwk_len; j++) {
+ (void) sprintf(stmp, "0x%02x ", k->iwk_key[j]);
+ (void) strcat(tmp, stmp);
+ }
+ ATH_DEBUG((ATH_DBG_AUX, "ath: ath_initkeytable(): "
+ "key%d val=%s\n", i, tmp));
+#endif /* DEBUG */
+ bzero(&hk, sizeof (hk));
+ hk.kv_type = HAL_CIPHER_WEP;
+ hk.kv_len = k->iwk_len;
+ bcopy(k->iwk_key, hk.kv_val, k->iwk_len);
+ ATH_HAL_KEYSET(ah, i, &hk);
+ }
+ }
+}
+
+void
+ath_reset(ath_t *asc)
+{
+ ieee80211com_t *isc = (ieee80211com_t *)asc;
+ struct ath_hal *ah = asc->asc_ah;
+ struct ieee80211channel *ch;
+ HAL_STATUS status;
+ HAL_CHANNEL hchan;
+
+ /*
+ * Convert to a HAL channel description with the flags
+ * constrained to reflect the current operating mode.
+ */
+ ch = isc->isc_ibss_chan;
+ hchan.channel = ch->ich_freq;
+ hchan.channelFlags = ath_chan2flags(isc, ch);
+
+ ATH_HAL_INTRSET(ah, 0); /* disable interrupts */
+ ath_draintxq(asc); /* stop xmit side */
+ if (asc->asc_invalid == 0)
+ ath_stoprecv(asc); /* stop recv side */
+ /* indicate channel change so we do a full reset */
+ if (!ATH_HAL_RESET(ah, (HAL_OPMODE)isc->isc_opmode, &hchan,
+ AH_TRUE, &status)) {
+ ath_problem("ath: ath_reset(): "
+ "reseting hardware failed, HAL status %u\n", status);
+ }
+ if (asc->asc_invalid == 0) {
+ ath_initkeytable(asc);
+ if (ath_startrecv(asc) != 0) /* restart recv */
+ ath_problem("ath: ath_reset(): "
+ "starting receiving logic failed\n");
+ if (isc->isc_state == IEEE80211_S_RUN) {
+ ath_beacon_config(asc); /* restart beacons */
+ }
+ ATH_HAL_INTRSET(ah, asc->asc_imask);
+ }
+}