summaryrefslogtreecommitdiffstats
path: root/sys/dev/ath
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2008-12-07 19:26:34 +0000
committersam <sam@FreeBSD.org>2008-12-07 19:26:34 +0000
commit21bf62a23b5727b1a937f1a3ffbdaefe11e9fa2a (patch)
treecaddb7677de0211f26e5f3ecf02274aa7f18e34f /sys/dev/ath
parent4d749b8bfc111aa31f43c813c441147c6ff13004 (diff)
downloadFreeBSD-src-21bf62a23b5727b1a937f1a3ffbdaefe11e9fa2a.zip
FreeBSD-src-21bf62a23b5727b1a937f1a3ffbdaefe11e9fa2a.tar.gz
New periodic calibration scheme needed for 11n parts that have
multiple algorithms and potentially collect multiple samples. Instead of a single calibration interval we now have short and long intervals; the long interval roughly corresponds to the previous single interval. The short interval is used to speedup collection of samples and happens much quicker. We make calls using the short interval until we're told the calibration work is complete at which point we fallback to the long interval. In addition there is a much longer reset interval used to flush all calibration state and cause everthing to start anew. With these changes you can also disable calibration entirely by setting the long interval to zero.
Diffstat (limited to 'sys/dev/ath')
-rw-r--r--sys/dev/ath/if_ath.c129
-rw-r--r--sys/dev/ath/if_athvar.h17
2 files changed, 92 insertions, 54 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 4cd1001..8301fbf 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -219,9 +219,15 @@ static void ath_announce(struct ath_softc *);
SYSCTL_DECL(_hw_ath);
/* XXX validate sysctl values */
-static int ath_calinterval = 30; /* calibrate every 30 secs */
-SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
- 0, "chip calibration interval (secs)");
+static int ath_longcalinterval = 30; /* long cals every 30 secs */
+SYSCTL_INT(_hw_ath, OID_AUTO, longcal, CTLFLAG_RW, &ath_longcalinterval,
+ 0, "long chip calibration interval (secs)");
+static int ath_shortcalinterval = 100; /* short cals every 100 ms */
+SYSCTL_INT(_hw_ath, OID_AUTO, shortcal, CTLFLAG_RW, &ath_shortcalinterval,
+ 0, "short chip calibration interval (msecs)");
+static int ath_resetcalinterval = 20*60; /* reset cal state 20 mins */
+SYSCTL_INT(_hw_ath, OID_AUTO, resetcal, CTLFLAG_RW, &ath_resetcalinterval,
+ 0, "reset chip calibration results (secs)");
static int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */
SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf,
@@ -1433,8 +1439,9 @@ ath_init(void *arg)
* state cached in the driver.
*/
sc->sc_diversity = ath_hal_getdiversity(ah);
- sc->sc_calinterval = 1;
- sc->sc_caltries = 0;
+ sc->sc_lastlongcal = 0;
+ sc->sc_resetcal = 1;
+ sc->sc_lastcalreset = 0;
/*
* Setup the hardware after reset: the key cache
@@ -1566,8 +1573,6 @@ ath_reset(struct ifnet *ifp)
if_printf(ifp, "%s: unable to reset hardware; hal status %u\n",
__func__, status);
sc->sc_diversity = ath_hal_getdiversity(ah);
- sc->sc_calinterval = 1;
- sc->sc_caltries = 0;
if (ath_startrecv(sc) != 0) /* restart recv */
if_printf(ifp, "%s: unable to start recv logic\n", __func__);
/*
@@ -5493,8 +5498,6 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
}
sc->sc_curchan = hchan;
sc->sc_diversity = ath_hal_getdiversity(ah);
- sc->sc_calinterval = 1;
- sc->sc_caltries = 0;
/*
* Re-enable rx framework.
@@ -5528,54 +5531,76 @@ ath_calibrate(void *arg)
{
struct ath_softc *sc = arg;
struct ath_hal *ah = sc->sc_ah;
- HAL_BOOL iqCalDone;
-
- sc->sc_stats.ast_per_cal++;
+ struct ifnet *ifp = sc->sc_ifp;
+ HAL_BOOL longCal, isCalDone;
+ int nextcal;
- if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) {
+ longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz);
+ if (longCal) {
+ sc->sc_stats.ast_per_cal++;
+ if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) {
+ /*
+ * Rfgain is out of bounds, reset the chip
+ * to load new gain values.
+ */
+ DPRINTF(sc, ATH_DEBUG_CALIBRATE,
+ "%s: rfgain change\n", __func__);
+ sc->sc_stats.ast_per_rfgain++;
+ ath_reset(ifp);
+ }
/*
- * Rfgain is out of bounds, reset the chip
- * to load new gain values.
+ * If this long cal is after an idle period, then
+ * reset the data collection state so we start fresh.
*/
- DPRINTF(sc, ATH_DEBUG_CALIBRATE,
- "%s: rfgain change\n", __func__);
- sc->sc_stats.ast_per_rfgain++;
- ath_reset(sc->sc_ifp);
+ if (sc->sc_resetcal) {
+ (void) ath_hal_calreset(ah, &sc->sc_curchan);
+ sc->sc_lastcalreset = ticks;
+ sc->sc_resetcal = 0;
+ }
}
- if (!ath_hal_calibrate(ah, &sc->sc_curchan, &iqCalDone)) {
+ if (ath_hal_calibrateN(ah, &sc->sc_curchan, longCal, &isCalDone)) {
+ if (longCal) {
+ /*
+ * Calibrate noise floor data again in case of change.
+ */
+ ath_hal_process_noisefloor(ah);
+ }
+ } else {
DPRINTF(sc, ATH_DEBUG_ANY,
"%s: calibration of channel %u failed\n",
__func__, sc->sc_curchan.channel);
sc->sc_stats.ast_per_calfail++;
}
- /*
- * Calibrate noise floor data again in case of change.
- */
- ath_hal_process_noisefloor(ah);
- /*
- * Poll more frequently when the IQ calibration is in
- * progress to speedup loading the final settings.
- * We temper this aggressive polling with an exponential
- * back off after 4 tries up to ath_calinterval.
- */
- if (iqCalDone || sc->sc_calinterval >= ath_calinterval) {
- sc->sc_caltries = 0;
- sc->sc_calinterval = ath_calinterval;
- } else if (sc->sc_caltries > 4) {
- sc->sc_caltries = 0;
- sc->sc_calinterval <<= 1;
- if (sc->sc_calinterval > ath_calinterval)
- sc->sc_calinterval = ath_calinterval;
- }
- KASSERT(0 < sc->sc_calinterval && sc->sc_calinterval <= ath_calinterval,
- ("bad calibration interval %u", sc->sc_calinterval));
-
- DPRINTF(sc, ATH_DEBUG_CALIBRATE,
- "%s: next +%u (%siqCalDone tries %u)\n", __func__,
- sc->sc_calinterval, iqCalDone ? "" : "!", sc->sc_caltries);
- sc->sc_caltries++;
- callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
- ath_calibrate, sc);
+ if (!isCalDone) {
+ /*
+ * Use a shorter interval to potentially collect multiple
+ * data samples required to complete calibration. Once
+ * we're told the work is done we drop back to a longer
+ * interval between requests. We're more aggressive doing
+ * work when operating as an AP to improve operation right
+ * after startup.
+ */
+ nextcal = (1000*ath_shortcalinterval)/hz;
+ if (sc->sc_opmode != HAL_M_HOSTAP)
+ nextcal *= 10;
+ } else {
+ nextcal = ath_longcalinterval*hz;
+ sc->sc_lastlongcal = ticks;
+ if (sc->sc_lastcalreset == 0)
+ sc->sc_lastcalreset = sc->sc_lastlongcal;
+ else if (ticks - sc->sc_lastcalreset >= ath_resetcalinterval*hz)
+ sc->sc_resetcal = 1; /* setup reset next trip */
+ }
+
+ if (nextcal != 0) {
+ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: next +%u (%sisCalDone)\n",
+ __func__, nextcal, isCalDone ? "" : "!");
+ callout_reset(&sc->sc_cal_ch, nextcal, ath_calibrate, sc);
+ } else {
+ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n",
+ __func__);
+ /* NB: don't rearm timer */
+ }
}
static void
@@ -5803,10 +5828,12 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* Finally, start any timers and the task q thread
* (in case we didn't go through SCAN state).
*/
- if (sc->sc_calinterval != 0) {
+ if (ath_longcalinterval != 0) {
/* start periodic recalibration timer */
- callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
- ath_calibrate, sc);
+ callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc);
+ } else {
+ DPRINTF(sc, ATH_DEBUG_CALIBRATE,
+ "%s: calibration disabled\n", __func__);
}
taskqueue_unblock(sc->sc_tq);
} else if (nstate == IEEE80211_S_INIT) {
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index 87ebb5d..0f08eec 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -248,7 +248,8 @@ struct ath_softc {
sc_swbmiss : 1,/* sta mode using sw bmiss */
sc_stagbeacons:1,/* use staggered beacons */
sc_wmetkipmic:1,/* can do WME+TKIP MIC */
- sc_resume_up: 1;/* on resume, start all vaps */
+ sc_resume_up: 1,/* on resume, start all vaps */
+ sc_resetcal : 1;/* reset cal state next trip */
uint32_t sc_eerd; /* regdomain from EEPROM */
uint32_t sc_eecc; /* country code from EEPROM */
/* rate tables */
@@ -334,8 +335,8 @@ struct ath_softc {
int sc_nbcnvaps; /* # vaps with beacons */
struct callout sc_cal_ch; /* callout handle for cals */
- int sc_calinterval; /* current polling interval */
- int sc_caltries; /* cals at current interval */
+ int sc_lastlongcal; /* last long cal completed */
+ int sc_lastcalreset;/* last cal reset done */
HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */
};
@@ -438,6 +439,16 @@ void ath_intr(void *);
((*(_ah)->ah_setChannel)((_ah), (_chan)))
#define ath_hal_calibrate(_ah, _chan, _iqcal) \
((*(_ah)->ah_perCalibration)((_ah), (_chan), (_iqcal)))
+#if HAL_ABI_VERSION >= 0x08111000
+#define ath_hal_calibrateN(_ah, _chan, _lcal, _isdone) \
+ ((*(_ah)->ah_perCalibrationN)((_ah), (_chan), 0x1, (_lcal), (_isdone)))
+#define ath_hal_calreset(_ah, _chan) \
+ ((*(_ah)->ah_resetCalValid)((_ah), (_chan)))
+#else
+#define ath_hal_calibrateN(_ah, _chan, _lcal, _isdone) \
+ ath_hal_calibrate(_ah, _chan, _isdone)
+#define ath_hal_calreset(_ah, _chan) (0)
+#endif
#define ath_hal_setledstate(_ah, _state) \
((*(_ah)->ah_setLedState)((_ah), (_state)))
#define ath_hal_beaconinit(_ah, _nextb, _bperiod) \
OpenPOWER on IntegriCloud