Date: Sun, 7 Dec 2008 19:26:34 +0000 (UTC) From: Sam Leffler <sam@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r185744 - head/sys/dev/ath Message-ID: <200812071926.mB7JQYud033765@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sam Date: Sun Dec 7 19:26:34 2008 New Revision: 185744 URL: http://svn.freebsd.org/changeset/base/185744 Log: 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. Modified: head/sys/dev/ath/if_ath.c head/sys/dev/ath/if_athvar.h Modified: head/sys/dev/ath/if_ath.c ============================================================================== --- head/sys/dev/ath/if_ath.c Sun Dec 7 19:17:33 2008 (r185743) +++ head/sys/dev/ath/if_ath.c Sun Dec 7 19:26:34 2008 (r185744) @@ -219,9 +219,15 @@ static void ath_announce(struct ath_soft 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, struc } 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, e * 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) { Modified: head/sys/dev/ath/if_athvar.h ============================================================================== --- head/sys/dev/ath/if_athvar.h Sun Dec 7 19:17:33 2008 (r185743) +++ head/sys/dev/ath/if_athvar.h Sun Dec 7 19:26:34 2008 (r185744) @@ -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) \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200812071926.mB7JQYud033765>