iPXE
ath9k_main.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008-2011 Atheros Communications Inc.
00003  *
00004  * Modified for iPXE by Scott K Logan <logans@cottsay.net> July 2011
00005  * Original from Linux kernel 3.0.1
00006  *
00007  * Permission to use, copy, modify, and/or distribute this software for any
00008  * purpose with or without fee is hereby granted, provided that the above
00009  * copyright notice and this permission notice appear in all copies.
00010  *
00011  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00012  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00013  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00014  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00015  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00016  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00017  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00018  */
00019 
00020 #include <ipxe/io.h>
00021 
00022 #include "ath9k.h"
00023 
00024 static void ath9k_bss_info_changed(struct net80211_device *dev, u32 changed);
00025 
00026 int ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
00027 {
00028         int ret;
00029 
00030         ret = ath9k_hw_setpower(sc->sc_ah, mode);
00031 
00032         return ret;
00033 }
00034 
00035 static void ath_start_ani(struct ath_common *common)
00036 {
00037         struct ath_hw *ah = common->ah;
00038         unsigned long timestamp = ( currticks() * 1000 ) / TICKS_PER_SEC;
00039         struct ath_softc *sc = (struct ath_softc *) common->priv;
00040 
00041         if (!(sc->sc_flags & SC_OP_ANI_RUN))
00042                 return;
00043 
00044         if (sc->sc_flags & SC_OP_OFFCHANNEL)
00045                 return;
00046 
00047         common->ani.longcal_timer = timestamp;
00048         common->ani.shortcal_timer = timestamp;
00049         common->ani.checkani_timer = timestamp;
00050 
00051         common->ani.timer = timestamp + ah->config.ani_poll_interval;
00052 }
00053 
00054 static void ath_update_survey_nf(struct ath_softc *sc, int channel)
00055 {
00056         struct ath_hw *ah = sc->sc_ah;
00057         struct ath9k_channel *chan = &ah->channels[channel];
00058         struct survey_info *survey = &sc->survey[channel];
00059 
00060         if (chan->noisefloor) {
00061                 survey->filled |= SURVEY_INFO_NOISE_DBM;
00062                 survey->noise = chan->noisefloor;
00063         }
00064 }
00065 
00066 /*
00067  * Updates the survey statistics and returns the busy time since last
00068  * update in %, if the measurement duration was long enough for the
00069  * result to be useful, -1 otherwise.
00070  */
00071 static int ath_update_survey_stats(struct ath_softc *sc)
00072 {
00073         struct ath_hw *ah = sc->sc_ah;
00074         struct ath_common *common = ath9k_hw_common(ah);
00075         int pos = ah->curchan - &ah->channels[0];
00076         struct survey_info *survey = &sc->survey[pos];
00077         struct ath_cycle_counters *cc = &common->cc_survey;
00078         unsigned int div = common->clockrate * 1000;
00079         int ret = 0;
00080 
00081         if (!ah->curchan)
00082                 return -1;
00083 
00084         if (ah->power_mode == ATH9K_PM_AWAKE)
00085                 ath_hw_cycle_counters_update(common);
00086 
00087         if (cc->cycles > 0) {
00088                 survey->filled |= SURVEY_INFO_CHANNEL_TIME |
00089                         SURVEY_INFO_CHANNEL_TIME_BUSY |
00090                         SURVEY_INFO_CHANNEL_TIME_RX |
00091                         SURVEY_INFO_CHANNEL_TIME_TX;
00092                 survey->channel_time += cc->cycles / div;
00093                 survey->channel_time_busy += cc->rx_busy / div;
00094                 survey->channel_time_rx += cc->rx_frame / div;
00095                 survey->channel_time_tx += cc->tx_frame / div;
00096         }
00097 
00098         if (cc->cycles < div)
00099                 return -1;
00100 
00101         if (cc->cycles > 0)
00102                 ret = cc->rx_busy * 100 / cc->cycles;
00103 
00104         memset(cc, 0, sizeof(*cc));
00105 
00106         ath_update_survey_nf(sc, pos);
00107 
00108         return ret;
00109 }
00110 
00111 /*
00112  * Set/change channels.  If the channel is really being changed, it's done
00113  * by reseting the chip.  To accomplish this we must first cleanup any pending
00114  * DMA, then restart stuff.
00115 */
00116 int ath_set_channel(struct ath_softc *sc, struct net80211_device *dev,
00117                     struct ath9k_channel *hchan)
00118 {
00119         struct ath_hw *ah = sc->sc_ah;
00120         struct ath_common *common = ath9k_hw_common(ah);
00121         int fastcc __unused = 1, stopped __unused;
00122         struct net80211_channel *channel = dev->channels + dev->channel;
00123         struct ath9k_hw_cal_data *caldata = NULL;
00124         int r;
00125 
00126         if (sc->sc_flags & SC_OP_INVALID)
00127                 return -EIO;
00128 
00129         sc->hw_busy_count = 0;
00130 
00131         common->ani.timer = 0;
00132         sc->tx_complete_work_timer = 0;
00133         sc->hw_pll_work_timer = 0;
00134 
00135         /*
00136          * This is only performed if the channel settings have
00137          * actually changed.
00138          *
00139          * To switch channels clear any pending DMA operations;
00140          * wait long enough for the RX fifo to drain, reset the
00141          * hardware at the new frequency, and then re-enable
00142          * the relevant bits of the h/w.
00143          */
00144         ath9k_hw_disable_interrupts(ah);
00145         stopped = ath_drain_all_txq(sc, 0);
00146 
00147         if (!ath_stoprecv(sc))
00148                 stopped = 0;
00149 
00150         if (!ath9k_hw_check_alive(ah))
00151                 stopped = 0;
00152 
00153         /* XXX: do not flush receive queue here. We don't want
00154          * to flush data frames already in queue because of
00155          * changing channel. */
00156 
00157         if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
00158                 caldata = &sc->caldata;
00159 
00160         DBG2("ath9k: "
00161                 "(%d MHz) -> (%d MHz)\n",
00162                 sc->sc_ah->curchan->channel,
00163                 channel->center_freq);
00164 
00165         r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
00166         if (r) {
00167                 DBG("ath9k: "
00168                         "Unable to reset channel (%d MHz), reset status %d\n",
00169                         channel->center_freq, r);
00170                 goto ps_restore;
00171         }
00172 
00173         if (ath_startrecv(sc) != 0) {
00174                 DBG("ath9k: Unable to restart recv logic\n");
00175                 r = -EIO;
00176                 goto ps_restore;
00177         }
00178 
00179         ath9k_cmn_update_txpow(ah, sc->curtxpow,
00180                                sc->config.txpowlimit, &sc->curtxpow);
00181         ath9k_hw_set_interrupts(ah, ah->imask);
00182 
00183         if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
00184                 sc->tx_complete_work(sc);
00185                 sc->hw_pll_work_timer = (currticks() * 1000 ) / TICKS_PER_SEC + 500;
00186                 ath_start_ani(common);
00187         }
00188 
00189  ps_restore:
00190         return r;
00191 }
00192 
00193 /*
00194  *  This routine performs the periodic noise floor calibration function
00195  *  that is used to adjust and optimize the chip performance.  This
00196  *  takes environmental changes (location, temperature) into account.
00197  *  When the task is complete, it reschedules itself depending on the
00198  *  appropriate interval that was calculated.
00199  */
00200 void ath_ani_calibrate(struct ath_softc *sc)
00201 {
00202         struct ath_hw *ah = sc->sc_ah;
00203         struct ath_common *common = ath9k_hw_common(ah);
00204         int longcal = 0;
00205         int shortcal = 0;
00206         int aniflag = 0;
00207         unsigned int timestamp = (currticks() * 1000 ) / TICKS_PER_SEC;
00208         u32 cal_interval, short_cal_interval, long_cal_interval;
00209 
00210         if (ah->caldata && ah->caldata->nfcal_interference)
00211                 long_cal_interval = ATH_LONG_CALINTERVAL_INT;
00212         else
00213                 long_cal_interval = ATH_LONG_CALINTERVAL;
00214 
00215         short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
00216 
00217         /* Only calibrate if awake */
00218         if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
00219                 goto set_timer;
00220 
00221         /* Long calibration runs independently of short calibration. */
00222         if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
00223                 longcal = 1;
00224                 DBG2("ath9k: longcal @%d\n", timestamp);
00225                 common->ani.longcal_timer = timestamp;
00226         }
00227 
00228         /* Short calibration applies only while caldone is false */
00229         if (!common->ani.caldone) {
00230                 if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
00231                         shortcal = 1;
00232                         DBG2("ath9k: "
00233                                 "shortcal @%d\n", timestamp);
00234                         common->ani.shortcal_timer = timestamp;
00235                         common->ani.resetcal_timer = timestamp;
00236                 }
00237         } else {
00238                 if ((timestamp - common->ani.resetcal_timer) >=
00239                     ATH_RESTART_CALINTERVAL) {
00240                         common->ani.caldone = ath9k_hw_reset_calvalid(ah);
00241                         if (common->ani.caldone)
00242                                 common->ani.resetcal_timer = timestamp;
00243                 }
00244         }
00245 
00246         /* Verify whether we must check ANI */
00247         if ((timestamp - common->ani.checkani_timer) >=
00248              ah->config.ani_poll_interval) {
00249                 aniflag = 1;
00250                 common->ani.checkani_timer = timestamp;
00251         }
00252 
00253         /* Skip all processing if there's nothing to do. */
00254         if (longcal || shortcal || aniflag) {
00255                 /* Call ANI routine if necessary */
00256                 if (aniflag) {
00257                         ath9k_hw_ani_monitor(ah, ah->curchan);
00258                         ath_update_survey_stats(sc);
00259                 }
00260 
00261                 /* Perform calibration if necessary */
00262                 if (longcal || shortcal) {
00263                         common->ani.caldone =
00264                                 ath9k_hw_calibrate(ah,
00265                                                    ah->curchan,
00266                                                    common->rx_chainmask,
00267                                                    longcal);
00268                 }
00269         }
00270 
00271 set_timer:
00272         /*
00273         * Set timer interval based on previous results.
00274         * The interval must be the shortest necessary to satisfy ANI,
00275         * short calibration and long calibration.
00276         */
00277         cal_interval = ATH_LONG_CALINTERVAL;
00278         if (sc->sc_ah->config.enable_ani)
00279                 cal_interval = min(cal_interval,
00280                                    (u32)ah->config.ani_poll_interval);
00281         if (!common->ani.caldone)
00282                 cal_interval = min(cal_interval, (u32)short_cal_interval);
00283 
00284         common->ani.timer = timestamp + cal_interval;
00285 }
00286 
00287 void ath_hw_check(struct ath_softc *sc)
00288 {
00289         int busy;
00290 
00291         if (ath9k_hw_check_alive(sc->sc_ah))
00292                 goto out;
00293 
00294         busy = ath_update_survey_stats(sc);
00295 
00296         DBG("ath9k: Possible baseband hang, "
00297                 "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
00298         if (busy >= 99) {
00299                 if (++sc->hw_busy_count >= 3)
00300                         ath_reset(sc, 1);
00301         } else if (busy >= 0)
00302                 sc->hw_busy_count = 0;
00303 
00304 out:
00305         return;
00306 }
00307 
00308 static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
00309 {
00310         static int count;
00311 
00312         if (pll_sqsum >= 0x40000) {
00313                 count++;
00314                 if (count == 3) {
00315                         /* Rx is hung for more than 500ms. Reset it */
00316                         DBG("ath9k: "
00317                                 "Possible RX hang, resetting");
00318                         ath_reset(sc, 1);
00319                         count = 0;
00320                 }
00321         } else
00322                 count = 0;
00323 }
00324 
00325 void ath_hw_pll_work(struct ath_softc *sc)
00326 {
00327         u32 pll_sqsum;
00328 
00329         if (AR_SREV_9485(sc->sc_ah)) {
00330                 pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
00331 
00332                 ath_hw_pll_rx_hang_check(sc, pll_sqsum);
00333 
00334                 sc->hw_pll_work_timer = (currticks() * 1000 ) / TICKS_PER_SEC + 200;
00335         }
00336 }
00337 
00338 
00339 void ath9k_tasklet(struct ath_softc *sc)
00340 {
00341         struct ath_hw *ah = sc->sc_ah;
00342 
00343         u32 status = sc->intrstatus;
00344         u32 rxmask;
00345 
00346         if ((status & ATH9K_INT_FATAL) ||
00347             (status & ATH9K_INT_BB_WATCHDOG)) {
00348                 ath_reset(sc, 1);
00349                 return;
00350         }
00351 
00352         rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
00353 
00354         if (status & rxmask) {
00355                 ath_rx_tasklet(sc, 0, 0);
00356         }
00357 
00358         if (status & ATH9K_INT_TX) {
00359                 ath_tx_tasklet(sc);
00360         }
00361 
00362         /* re-enable hardware interrupt */
00363         ath9k_hw_enable_interrupts(ah);
00364 }
00365 
00366 void ath_isr(struct net80211_device *dev)
00367 {
00368 #define SCHED_INTR (                            \
00369                 ATH9K_INT_FATAL |               \
00370                 ATH9K_INT_BB_WATCHDOG |         \
00371                 ATH9K_INT_RXORN |               \
00372                 ATH9K_INT_RXEOL |               \
00373                 ATH9K_INT_RX |                  \
00374                 ATH9K_INT_RXLP |                \
00375                 ATH9K_INT_RXHP |                \
00376                 ATH9K_INT_TX |                  \
00377                 ATH9K_INT_BMISS |               \
00378                 ATH9K_INT_CST |                 \
00379                 ATH9K_INT_TSFOOR |              \
00380                 ATH9K_INT_GENTIMER)
00381 
00382         struct ath_softc *sc = dev->priv;
00383         struct ath_hw *ah = sc->sc_ah;
00384         struct ath_common *common = ath9k_hw_common(ah);
00385         enum ath9k_int status;
00386         unsigned long timestamp = (currticks() * 1000 ) / TICKS_PER_SEC;
00387         int sched = 0;
00388 
00389         /*
00390          * The hardware is not ready/present, don't
00391          * touch anything. Note this can happen early
00392          * on if the IRQ is shared.
00393          */
00394         if (sc->sc_flags & SC_OP_INVALID)
00395                 return;
00396 
00397 
00398         /* Check calibration */
00399         if(timestamp >= (unsigned int)common->ani.timer && common->ani.timer)
00400                 ath_ani_calibrate(sc);
00401 
00402         /* Check tx_complete_work */
00403         if(timestamp >= (unsigned int)sc->tx_complete_work_timer && sc->tx_complete_work_timer)
00404                 sc->tx_complete_work(sc);
00405 
00406         /* Check hw_pll_work */
00407         if(timestamp >= (unsigned int)sc->hw_pll_work_timer && sc->hw_pll_work_timer)
00408                 sc->hw_pll_work(sc);
00409 
00410         /* shared irq, not for us */
00411 
00412         if (!ath9k_hw_intrpend(ah))
00413                 return;
00414 
00415         /*
00416          * Figure out the reason(s) for the interrupt.  Note
00417          * that the hal returns a pseudo-ISR that may include
00418          * bits we haven't explicitly enabled so we mask the
00419          * value to insure we only process bits we requested.
00420          */
00421         ath9k_hw_getisr(ah, &status);   /* NB: clears ISR too */
00422         status &= ah->imask;    /* discard unasked-for bits */
00423 
00424         /*
00425          * If there are no status bits set, then this interrupt was not
00426          * for me (should have been caught above).
00427          */
00428         if (!status)
00429                 return;
00430 
00431         /* Cache the status */
00432         sc->intrstatus = status;
00433 
00434         if (status & SCHED_INTR)
00435                 sched = 1;
00436 
00437         /*
00438          * If a FATAL or RXORN interrupt is received, we have to reset the
00439          * chip immediately.
00440          */
00441         if ((status & ATH9K_INT_FATAL) || (status & ATH9K_INT_RXORN))
00442                 goto chip_reset;
00443 
00444         if (status & ATH9K_INT_TXURN)
00445                 ath9k_hw_updatetxtriglevel(ah, 1);
00446 
00447         if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
00448                 if (status & ATH9K_INT_TIM_TIMER) {
00449                         if (sc->ps_idle)
00450                                 goto chip_reset;
00451                         /* Clear RxAbort bit so that we can
00452                          * receive frames */
00453                         ath9k_setpower(sc, ATH9K_PM_AWAKE);
00454                         ath9k_hw_setrxabort(sc->sc_ah, 0);
00455                         sc->ps_flags |= PS_WAIT_FOR_BEACON;
00456                 }
00457 
00458 chip_reset:
00459 
00460         if (sched) {
00461                 /* turn off every interrupt */
00462                 ath9k_hw_disable_interrupts(ah);
00463                 sc->intr_tq(sc);
00464         }
00465 
00466         return;
00467 
00468 #undef SCHED_INTR
00469 }
00470 
00471 void ath_radio_disable(struct ath_softc *sc, struct net80211_device *dev)
00472 {
00473         struct ath_hw *ah = sc->sc_ah;
00474         struct net80211_channel *channel = dev->channels + dev->channel;
00475         int r;
00476 
00477         sc->hw_pll_work_timer = 0;
00478 
00479         /*
00480          * Keep the LED on when the radio is disabled
00481          * during idle unassociated state.
00482          */
00483         if (!sc->ps_idle) {
00484                 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
00485                 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
00486         }
00487 
00488         /* Disable interrupts */
00489         ath9k_hw_disable_interrupts(ah);
00490 
00491         ath_drain_all_txq(sc, 0);       /* clear pending tx frames */
00492 
00493         ath_stoprecv(sc);               /* turn off frame recv */
00494         ath_flushrecv(sc);              /* flush recv queue */
00495 
00496         if (!ah->curchan)
00497                 ah->curchan = ath9k_cmn_get_curchannel(dev, ah);
00498 
00499         r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, 0);
00500         if (r) {
00501                 DBG("ath9k: "
00502                         "Unable to reset channel (%d MHz), reset status %d\n",
00503                         channel->center_freq, r);
00504         }
00505 
00506         ath9k_hw_phy_disable(ah);
00507 
00508         ath9k_hw_configpcipowersave(ah, 1, 1);
00509 }
00510 
00511 int ath_reset(struct ath_softc *sc, int retry_tx)
00512 {
00513         struct ath_hw *ah = sc->sc_ah;
00514         struct ath_common *common = ath9k_hw_common(ah);
00515         int r;
00516 
00517         sc->hw_busy_count = 0;
00518 
00519         /* Stop ANI */
00520         common->ani.timer = 0;
00521 
00522         ath9k_hw_disable_interrupts(ah);
00523         ath_drain_all_txq(sc, retry_tx);
00524 
00525         ath_stoprecv(sc);
00526         ath_flushrecv(sc);
00527 
00528         r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, 0);
00529         if (r)
00530                 DBG("ath9k: "
00531                         "Unable to reset hardware; reset status %d\n", r);
00532 
00533         if (ath_startrecv(sc) != 0)
00534                 DBG("ath9k: Unable to start recv logic\n");
00535 
00536         /*
00537          * We may be doing a reset in response to a request
00538          * that changes the channel so update any state that
00539          * might change as a result.
00540          */
00541         ath9k_cmn_update_txpow(ah, sc->curtxpow,
00542                                sc->config.txpowlimit, &sc->curtxpow);
00543 
00544         ath9k_hw_set_interrupts(ah, ah->imask);
00545 
00546         if (retry_tx) {
00547                 int i;
00548                 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
00549                         if (ATH_TXQ_SETUP(sc, i)) {
00550                                 ath_txq_schedule(sc, &sc->tx.txq[i]);
00551                         }
00552                 }
00553         }
00554 
00555         /* Start ANI */
00556         ath_start_ani(common);
00557 
00558         return r;
00559 }
00560 
00561 /**********************/
00562 /* mac80211 callbacks */
00563 /**********************/
00564 
00565 static int ath9k_start(struct net80211_device *dev)
00566 {
00567         struct ath_softc *sc = dev->priv;
00568         struct ath_hw *ah = sc->sc_ah;
00569         struct ath_common *common = ath9k_hw_common(ah);
00570         struct net80211_channel *curchan = dev->channels + dev->channel;
00571         struct ath9k_channel *init_channel;
00572         int r;
00573 
00574         DBG("ath9k: "
00575                 "Starting driver with initial channel: %d MHz\n",
00576                 curchan->center_freq);
00577 
00578         /* setup initial channel */
00579         sc->chan_idx = curchan->hw_value;
00580 
00581         init_channel = ath9k_cmn_get_curchannel(dev, ah);
00582 
00583         /* Reset SERDES registers */
00584         ath9k_hw_configpcipowersave(ah, 0, 0);
00585 
00586         /*
00587          * The basic interface to setting the hardware in a good
00588          * state is ``reset''.  On return the hardware is known to
00589          * be powered up and with interrupts disabled.  This must
00590          * be followed by initialization of the appropriate bits
00591          * and then setup of the interrupt mask.
00592          */
00593         r = ath9k_hw_reset(ah, init_channel, ah->caldata, 0);
00594         if (r) {
00595                 DBG("ath9k: "
00596                         "Unable to reset hardware; reset status %d (freq %d MHz)\n",
00597                         r, curchan->center_freq);
00598                 goto mutex_unlock;
00599         }
00600 
00601         /*
00602          * This is needed only to setup initial state
00603          * but it's best done after a reset.
00604          */
00605         ath9k_cmn_update_txpow(ah, sc->curtxpow,
00606                         sc->config.txpowlimit, &sc->curtxpow);
00607 
00608         /*
00609          * Setup the hardware after reset:
00610          * The receive engine is set going.
00611          * Frame transmit is handled entirely
00612          * in the frame output path; there's nothing to do
00613          * here except setup the interrupt mask.
00614          */
00615         if (ath_startrecv(sc) != 0) {
00616                 DBG("ath9k: Unable to start recv logic\n");
00617                 r = -EIO;
00618                 goto mutex_unlock;
00619         }
00620 
00621         /* Setup our intr mask. */
00622         ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
00623                     ATH9K_INT_RXORN | ATH9K_INT_FATAL |
00624                     ATH9K_INT_GLOBAL;
00625 
00626         ah->imask |= ATH9K_INT_RX;
00627 
00628         sc->sc_flags &= ~SC_OP_INVALID;
00629         sc->sc_ah->is_monitoring = 0;
00630 
00631         ath9k_hw_set_interrupts(ah, ah->imask);
00632 
00633         sc->tx_complete_work(sc);
00634 
00635         if (ah->caps.pcie_lcr_extsync_en && common->bus_ops->extn_synch_en)
00636                 common->bus_ops->extn_synch_en(common);
00637 
00638 mutex_unlock:
00639         return r;
00640 }
00641 
00642 static int ath9k_tx(struct net80211_device *dev, struct io_buffer *iob)
00643 {
00644         struct ath_softc *sc = dev->priv;
00645         struct ath_tx_control txctl;
00646         int ret = 0;
00647 
00648         memset(&txctl, 0, sizeof(struct ath_tx_control));
00649         txctl.txq = sc->tx.txq_map[0];
00650 
00651         DBGIO("ath9k: transmitting packet, iob: %p\n", iob);
00652 
00653         ret = ath_tx_start(dev, iob, &txctl);
00654         if (ret) {
00655                 DBG("ath9k: TX failed\n");
00656                 goto exit;
00657         }
00658 
00659         return ret;
00660 exit:
00661         free_iob(iob);
00662         return ret;
00663 }
00664 
00665 static void ath9k_stop(struct net80211_device *dev)
00666 {
00667         struct ath_softc *sc = dev->priv;
00668         struct ath_hw *ah = sc->sc_ah;
00669 
00670         sc->tx_complete_work_timer = 0;
00671         sc->hw_pll_work_timer = 0;
00672 
00673         if (sc->sc_flags & SC_OP_INVALID) {
00674                 DBG("ath9k: Device not present\n");
00675                 return;
00676         }
00677 
00678         /* prevent tasklets to enable interrupts once we disable them */
00679         ah->imask &= ~ATH9K_INT_GLOBAL;
00680 
00681         /* make sure h/w will not generate any interrupt
00682          * before setting the invalid flag. */
00683         ath9k_hw_disable_interrupts(ah);
00684 
00685         if (!(sc->sc_flags & SC_OP_INVALID)) {
00686                 ath_drain_all_txq(sc, 0);
00687                 ath_stoprecv(sc);
00688                 ath9k_hw_phy_disable(ah);
00689         } else
00690                 sc->rx.rxlink = NULL;
00691 
00692         if (sc->rx.frag) {
00693                 free_iob(sc->rx.frag);
00694                 sc->rx.frag = NULL;
00695         }
00696 
00697         /* disable HAL and put h/w to sleep */
00698         ath9k_hw_disable(ah);
00699         ath9k_hw_configpcipowersave(ah, 1, 1);
00700 
00701         ath_radio_disable(sc, dev);
00702 
00703         sc->sc_flags |= SC_OP_INVALID;
00704 
00705         DBG("ath9k: Driver halt\n");
00706 }
00707 
00708 static int ath9k_config(struct net80211_device *dev, int changed)
00709 {
00710         struct ath_softc *sc = dev->priv;
00711         struct ath_hw *ah = sc->sc_ah;
00712 
00713         if ((changed & NET80211_CFG_RATE) ||
00714                         (changed & NET80211_CFG_PHY_PARAMS)) {
00715                 int spmbl = (sc->sc_flags & SC_OP_PREAMBLE_SHORT) ? IEEE80211_TX_RC_USE_SHORT_PREAMBLE : 0;
00716                 u16 rate = dev->rates[dev->rate];
00717                 u16 slowrate = dev->rates[dev->rtscts_rate];
00718                 int i;
00719 
00720                 for (i = 0; i < NET80211_MAX_RATES; i++) {
00721                         if (sc->rates[i].bitrate == rate &&
00722                             (sc->rates[i].flags & spmbl))
00723                                 sc->hw_rix = i;
00724 
00725                         if (sc->rates[i].bitrate == slowrate &&
00726                             (sc->rates[i].flags & spmbl))
00727                                 sc->hw_rix = i;
00728                 }
00729         }
00730 
00731         ath9k_bss_info_changed(dev, changed);
00732 
00733         if (changed & NET80211_CFG_CHANNEL) {
00734                 struct net80211_channel *curchan = dev->channels + dev->channel;
00735                 int pos = curchan->hw_value;
00736                 int old_pos = -1;
00737 
00738                 if (ah->curchan)
00739                         old_pos = ah->curchan - &ah->channels[0];
00740 
00741                 sc->sc_flags &= ~SC_OP_OFFCHANNEL;
00742 
00743                 DBG2("ath9k: "
00744                         "Set channel: %d MHz\n",
00745                         curchan->center_freq);
00746 
00747                 ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
00748                                           curchan);
00749 
00750                 /* update survey stats for the old channel before switching */
00751                 ath_update_survey_stats(sc);
00752 
00753                 /*
00754                  * If the operating channel changes, change the survey in-use flags
00755                  * along with it.
00756                  * Reset the survey data for the new channel, unless we're switching
00757                  * back to the operating channel from an off-channel operation.
00758                  */
00759                 if (sc->cur_survey != &sc->survey[pos]) {
00760 
00761                         if (sc->cur_survey)
00762                                 sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
00763 
00764                         sc->cur_survey = &sc->survey[pos];
00765 
00766                         memset(sc->cur_survey, 0, sizeof(struct survey_info));
00767                         sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
00768                 } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
00769                         memset(&sc->survey[pos], 0, sizeof(struct survey_info));
00770                 }
00771 
00772                 if (ath_set_channel(sc, dev, &sc->sc_ah->channels[pos]) < 0) {
00773                         DBG("ath9k: Unable to set channel\n");
00774                         return -EINVAL;
00775                 }
00776 
00777                 /*
00778                  * The most recent snapshot of channel->noisefloor for the old
00779                  * channel is only available after the hardware reset. Copy it to
00780                  * the survey stats now.
00781                  */
00782                 if (old_pos >= 0)
00783                         ath_update_survey_nf(sc, old_pos);
00784         }
00785 
00786         if (changed & NET80211_CFG_CHANNEL) {
00787                 DBG2("ath9k: "
00788                         "Set power: %d\n", (dev->channels + dev->channel)->maxpower);
00789                 sc->config.txpowlimit = 2 * (dev->channels + dev->channel)->maxpower;
00790                 ath9k_cmn_update_txpow(ah, sc->curtxpow,
00791                                        sc->config.txpowlimit, &sc->curtxpow);
00792         }
00793 
00794         return 0;
00795 }
00796 
00797 static void ath9k_bss_iter(struct ath_softc *sc)
00798 {
00799         struct ath_common *common = ath9k_hw_common(sc->sc_ah);
00800 
00801         if (common->dev->state & NET80211_ASSOCIATED) {
00802                 sc->sc_flags |= SC_OP_PRIM_STA_VIF;
00803                 memcpy(common->curbssid, common->dev->bssid, ETH_ALEN);
00804                 common->curaid = common->dev->aid;
00805                 ath9k_hw_write_associd(sc->sc_ah);
00806                 DBG("ath9k: "
00807                         "Bss Info ASSOC %d, bssid: %pM\n",
00808                         common->dev->aid, common->curbssid);
00809 
00810                 /*
00811                  * Request a re-configuration of Beacon related timers
00812                  * on the receipt of the first Beacon frame (i.e.,
00813                  * after time sync with the AP).
00814                  */
00815                 sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
00816                 /* Reset rssi stats */
00817                 sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
00818                 sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
00819 
00820                 sc->sc_flags |= SC_OP_ANI_RUN;
00821                 ath_start_ani(common);
00822         }
00823 }
00824 
00825 static void ath9k_config_bss(struct ath_softc *sc)
00826 {
00827         struct ath_common *common = ath9k_hw_common(sc->sc_ah);
00828         struct net80211_device *dev = common->dev;
00829 
00830         /* Reconfigure bss info */
00831         if (!(dev->state & NET80211_ASSOCIATED)) {
00832                 DBG2("ath9k: "
00833                         "ath9k: Bss Info DISASSOC %d, bssid %pM\n",
00834                         common->curaid, common->curbssid);
00835                 sc->sc_flags &= ~(SC_OP_PRIM_STA_VIF | SC_OP_BEACONS);
00836                 memset(common->curbssid, 0, ETH_ALEN);
00837                 common->curaid = 0;
00838         }
00839 
00840         ath9k_bss_iter(sc);
00841 
00842         /*
00843          * None of station vifs are associated.
00844          * Clear bssid & aid
00845          */
00846         if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) {
00847                 ath9k_hw_write_associd(sc->sc_ah);
00848                 /* Stop ANI */
00849                 sc->sc_flags &= ~SC_OP_ANI_RUN;
00850                 common->ani.timer = 0;
00851         }
00852 }
00853 
00854 static void ath9k_bss_info_changed(struct net80211_device *dev,
00855                                    u32 changed)
00856 {
00857         struct ath_softc *sc = dev->priv;
00858         struct ath_hw *ah = sc->sc_ah;
00859         struct ath_common *common = ath9k_hw_common(ah);
00860         int slottime;
00861 
00862         if (changed & NET80211_CFG_ASSOC) {
00863                 ath9k_config_bss(sc);
00864 
00865                 DBG2("ath9k: BSSID: %pM aid: 0x%x\n",
00866                         common->curbssid, common->curaid);
00867         }
00868 
00869         if (changed & NET80211_CFG_PHY_PARAMS) {
00870                 if (dev->phy_flags & NET80211_PHY_USE_PROTECTION)
00871                         slottime = 9;
00872                 else
00873                         slottime = 20;
00874                 ah->slottime = slottime;
00875                 ath9k_hw_init_global_settings(ah);
00876 
00877                 DBG2("ath9k: BSS Changed PREAMBLE %d\n",
00878                                 !!(dev->phy_flags & NET80211_PHY_USE_SHORT_PREAMBLE));
00879                 if (dev->phy_flags & NET80211_PHY_USE_SHORT_PREAMBLE)
00880                         sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
00881                 else
00882                         sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
00883 
00884                 DBG2("ath9k: BSS Changed CTS PROT %d\n",
00885                         !!(dev->phy_flags & NET80211_PHY_USE_PROTECTION));
00886                 if ((dev->phy_flags & NET80211_PHY_USE_PROTECTION) &&
00887                     (dev->channels + dev->channel)->band != NET80211_BAND_5GHZ)
00888                         sc->sc_flags |= SC_OP_PROTECT_ENABLE;
00889                 else
00890                         sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
00891         }
00892 }
00893 
00894 static void ath9k_poll(struct net80211_device *dev)
00895 {
00896         ath_isr(dev);
00897 }
00898 
00899 static void ath9k_irq(struct net80211_device *dev, int enable)
00900 {
00901         struct ath_softc *sc = dev->priv;
00902         struct ath_hw *ah = sc->sc_ah;
00903 
00904         ah->ah_ier = enable ? AR_IER_ENABLE : AR_IER_DISABLE;
00905 
00906         ath9k_hw_set_interrupts(ah, ah->imask);
00907 }
00908 
00909 struct net80211_device_operations ath9k_ops = {
00910         .transmit       = ath9k_tx,
00911         .open           = ath9k_start,
00912         .close          = ath9k_stop,
00913         .config         = ath9k_config,
00914         .poll           = ath9k_poll,
00915         .irq            = ath9k_irq,
00916 };