aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath9k/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath9k/core.c')
-rw-r--r--drivers/net/wireless/ath9k/core.c145
1 files changed, 129 insertions, 16 deletions
diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c
index 6c433a4d003..c5033f6f42a 100644
--- a/drivers/net/wireless/ath9k/core.c
+++ b/drivers/net/wireless/ath9k/core.c
@@ -65,7 +65,7 @@ static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode)
for (i = 0; i < rt->rateCount; i++)
sc->sc_rixmap[rt->info[i].rateCode] = (u8) i;
- memzero(sc->sc_hwmap, sizeof(sc->sc_hwmap));
+ memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
for (i = 0; i < 256; i++) {
u8 ix = rt->rateCodeToIndex[i];
@@ -288,8 +288,6 @@ static int ath_stop(struct ath_softc *sc)
* hardware is gone (invalid).
*/
- if (!(sc->sc_flags & SC_OP_INVALID))
- ath9k_hw_set_interrupts(ah, 0);
ath_draintxq(sc, false);
if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_stoprecv(sc);
@@ -419,7 +417,7 @@ static void ath_chainmask_sel_init(struct ath_softc *sc, struct ath_node *an)
{
struct ath_chainmask_sel *cm = &an->an_chainmask_sel;
- memzero(cm, sizeof(struct ath_chainmask_sel));
+ memset(cm, 0, sizeof(struct ath_chainmask_sel));
cm->cur_tx_mask = sc->sc_tx_chainmask;
cm->cur_rx_mask = sc->sc_rx_chainmask;
@@ -492,6 +490,122 @@ void ath_update_chainmask(struct ath_softc *sc, int is_ht)
__func__, sc->sc_tx_chainmask, sc->sc_rx_chainmask);
}
+/*******/
+/* ANI */
+/*******/
+
+/*
+ * This routine performs the periodic noise floor calibration function
+ * that is used to adjust and optimize the chip performance. This
+ * takes environmental changes (location, temperature) into account.
+ * When the task is complete, it reschedules itself depending on the
+ * appropriate interval that was calculated.
+ */
+
+static void ath_ani_calibrate(unsigned long data)
+{
+ struct ath_softc *sc;
+ struct ath_hal *ah;
+ bool longcal = false;
+ bool shortcal = false;
+ bool aniflag = false;
+ unsigned int timestamp = jiffies_to_msecs(jiffies);
+ u32 cal_interval;
+
+ sc = (struct ath_softc *)data;
+ ah = sc->sc_ah;
+
+ /*
+ * don't calibrate when we're scanning.
+ * we are most likely not on our home channel.
+ */
+ if (sc->rx_filter & FIF_BCN_PRBRESP_PROMISC)
+ return;
+
+ /* Long calibration runs independently of short calibration. */
+ if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) {
+ longcal = true;
+ DPRINTF(sc, ATH_DBG_ANI, "%s: longcal @%lu\n",
+ __func__, jiffies);
+ sc->sc_ani.sc_longcal_timer = timestamp;
+ }
+
+ /* Short calibration applies only while sc_caldone is false */
+ if (!sc->sc_ani.sc_caldone) {
+ if ((timestamp - sc->sc_ani.sc_shortcal_timer) >=
+ ATH_SHORT_CALINTERVAL) {
+ shortcal = true;
+ DPRINTF(sc, ATH_DBG_ANI, "%s: shortcal @%lu\n",
+ __func__, jiffies);
+ sc->sc_ani.sc_shortcal_timer = timestamp;
+ sc->sc_ani.sc_resetcal_timer = timestamp;
+ }
+ } else {
+ if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
+ ATH_RESTART_CALINTERVAL) {
+ ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
+ &sc->sc_ani.sc_caldone);
+ if (sc->sc_ani.sc_caldone)
+ sc->sc_ani.sc_resetcal_timer = timestamp;
+ }
+ }
+
+ /* Verify whether we must check ANI */
+ if ((timestamp - sc->sc_ani.sc_checkani_timer) >=
+ ATH_ANI_POLLINTERVAL) {
+ aniflag = true;
+ sc->sc_ani.sc_checkani_timer = timestamp;
+ }
+
+ /* Skip all processing if there's nothing to do. */
+ if (longcal || shortcal || aniflag) {
+ /* Call ANI routine if necessary */
+ if (aniflag)
+ ath9k_hw_ani_monitor(ah, &sc->sc_halstats,
+ ah->ah_curchan);
+
+ /* Perform calibration if necessary */
+ if (longcal || shortcal) {
+ bool iscaldone = false;
+
+ if (ath9k_hw_calibrate(ah, ah->ah_curchan,
+ sc->sc_rx_chainmask, longcal,
+ &iscaldone)) {
+ if (longcal)
+ sc->sc_ani.sc_noise_floor =
+ ath9k_hw_getchan_noise(ah,
+ ah->ah_curchan);
+
+ DPRINTF(sc, ATH_DBG_ANI,
+ "%s: calibrate chan %u/%x nf: %d\n",
+ __func__,
+ ah->ah_curchan->channel,
+ ah->ah_curchan->channelFlags,
+ sc->sc_ani.sc_noise_floor);
+ } else {
+ DPRINTF(sc, ATH_DBG_ANY,
+ "%s: calibrate chan %u/%x failed\n",
+ __func__,
+ ah->ah_curchan->channel,
+ ah->ah_curchan->channelFlags);
+ }
+ sc->sc_ani.sc_caldone = iscaldone;
+ }
+ }
+
+ /*
+ * Set timer interval based on previous results.
+ * The interval must be the shortest necessary to satisfy ANI,
+ * short calibration and long calibration.
+ */
+
+ cal_interval = ATH_ANI_POLLINTERVAL;
+ if (!sc->sc_ani.sc_caldone)
+ cal_interval = min(cal_interval, (u32)ATH_SHORT_CALINTERVAL);
+
+ mod_timer(&sc->sc_ani.timer, jiffies + msecs_to_jiffies(cal_interval));
+}
+
/******************/
/* VAP management */
/******************/
@@ -528,7 +642,7 @@ int ath_vap_attach(struct ath_softc *sc,
if (avp == NULL)
return -ENOMEM;
- memzero(avp, sizeof(struct ath_vap));
+ memset(avp, 0, sizeof(struct ath_vap));
avp->av_if_data = if_data;
/* Set the VAP opmode */
avp->av_opmode = opmode;
@@ -678,12 +792,6 @@ int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan)
if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
sc->sc_imask |= ATH9K_INT_CST;
- /* Note: We disable MIB interrupts for now as we don't yet
- * handle processing ANI, otherwise you will get an interrupt
- * storm after about 7 hours of usage making the system unusable
- * with huge latency. Once we do have ANI processing included
- * we can re-enable this interrupt. */
-#if 0
/*
* Enable MIB interrupts when there are hardware phy counters.
* Note we only do this (at the moment) for station mode.
@@ -692,7 +800,6 @@ int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan)
((sc->sc_ah->ah_opmode == ATH9K_M_STA) ||
(sc->sc_ah->ah_opmode == ATH9K_M_IBSS)))
sc->sc_imask |= ATH9K_INT_MIB;
-#endif
/*
* Some hardware processes the TIM IE and fires an
* interrupt when the TIM bit is set. For hardware
@@ -993,6 +1100,10 @@ int ath_init(u16 devid, struct ath_softc *sc)
}
sc->sc_ah = ah;
+ /* Initializes the noise floor to a reasonable default value.
+ * Later on this will be updated during ANI processing. */
+ sc->sc_ani.sc_noise_floor = ATH_DEFAULT_NOISE_FLOOR;
+
/* Get the hardware key cache size. */
sc->sc_keymax = ah->ah_caps.keycache_size;
if (sc->sc_keymax > ATH_KEYMAX) {
@@ -1100,6 +1211,8 @@ int ath_init(u16 devid, struct ath_softc *sc)
goto bad2;
}
+ setup_timer(&sc->sc_ani.timer, ath_ani_calibrate, (unsigned long)sc);
+
sc->sc_rc = ath_rate_attach(ah);
if (sc->sc_rc == NULL) {
error = -EIO;
@@ -1221,7 +1334,7 @@ struct ath_node *ath_node_attach(struct ath_softc *sc, u8 *addr, int if_id)
an = kmalloc(sizeof(struct ath_node), GFP_ATOMIC);
if (an == NULL)
return NULL;
- memzero(an, sizeof(*an));
+ memset(an, 0, sizeof(*an));
an->an_sc = sc;
memcpy(an->an_addr, addr, ETH_ALEN);
@@ -1608,7 +1721,7 @@ int ath_descdma_setup(struct ath_softc *sc,
error = -ENOMEM;
goto fail2;
}
- memzero(bf, bsize);
+ memset(bf, 0, bsize);
dd->dd_bufptr = bf;
INIT_LIST_HEAD(head);
@@ -1640,7 +1753,7 @@ fail2:
pci_free_consistent(sc->pdev,
dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
fail:
- memzero(dd, sizeof(*dd));
+ memset(dd, 0, sizeof(*dd));
return error;
#undef ATH_DESC_4KB_BOUND_CHECK
#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
@@ -1665,7 +1778,7 @@ void ath_descdma_cleanup(struct ath_softc *sc,
INIT_LIST_HEAD(head);
kfree(dd->dd_bufptr);
- memzero(dd, sizeof(*dd));
+ memset(dd, 0, sizeof(*dd));
}
/*************/