Begin fleshing out some code to handle tracking PLCP error rates

in preparation for the scan based retune logic.

The linux iwlwifi driver does a rescan (onto a non-active channel)
to force an RF retune when the PLCP error rates exceed a certain threshold.

* Add code to track HT PLCP rate errors;
* Separate out the PLCP error count fetch and update so the delta
  can be used when checking for PLCP error rates;
* Implement the PLCP error logic from iwlwifi;
* For now, just print out whenever the error rate exceeds the
  threshold.

The actual scan based retune will take a bit more effort; the scan
command code right now assumes that a scan state is passed in.
This does need to change to be more flexible (both for this and
in preparation for scanning multiple channels at once.)

Tested:

* 5100 (STA mode)
* 2200 (STA mode)
* 2230 (STA mode)
This commit is contained in:
Adrian Chadd 2013-12-07 08:03:10 +00:00
parent 53fff962b6
commit 92d7ab9562
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=259061
2 changed files with 118 additions and 14 deletions

View File

@ -272,7 +272,10 @@ static int iwn4965_set_gains(struct iwn_softc *);
static int iwn5000_set_gains(struct iwn_softc *);
static void iwn_tune_sensitivity(struct iwn_softc *,
const struct iwn_rx_stats *);
static void iwn_save_stats_counters(struct iwn_softc *,
const struct iwn_stats *);
static int iwn_send_sensitivity(struct iwn_softc *);
static void iwn_check_rx_recovery(struct iwn_softc *, struct iwn_stats *);
static int iwn_set_pslevel(struct iwn_softc *, int, int, int);
static int iwn_send_btcoex(struct iwn_softc *);
static int iwn_send_advanced_btcoex(struct iwn_softc *);
@ -3187,12 +3190,38 @@ iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc,
if (calib->state == IWN_CALIB_STATE_ASSOC)
iwn_collect_noise(sc, &stats->rx.general);
else if (calib->state == IWN_CALIB_STATE_RUN)
else if (calib->state == IWN_CALIB_STATE_RUN) {
iwn_tune_sensitivity(sc, &stats->rx);
/*
* XXX TODO: Only run the RX recovery if we're associated!
*/
iwn_check_rx_recovery(sc, stats);
iwn_save_stats_counters(sc, stats);
}
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__);
}
/*
* Save the relevant statistic counters for the next calibration
* pass.
*/
static void
iwn_save_stats_counters(struct iwn_softc *sc, const struct iwn_stats *rs)
{
struct iwn_calib_state *calib = &sc->calib;
/* Save counters values for next call. */
calib->bad_plcp_cck = le32toh(rs->rx.cck.bad_plcp);
calib->fa_cck = le32toh(rs->rx.cck.fa);
calib->bad_plcp_ht = le32toh(rs->rx.ht.bad_plcp);
calib->bad_plcp_ofdm = le32toh(rs->rx.ofdm.bad_plcp);
calib->fa_ofdm = le32toh(rs->rx.ofdm.fa);
/* Last time we received these tick values */
sc->last_calib_ticks = ticks;
}
/*
* Process a TX_DONE firmware notification. Unfortunately, the 4965AGN
* and 5000 adapters have different incompatible TX status formats.
@ -5652,10 +5681,6 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm;
fa *= 200 * IEEE80211_DUR_TU; /* 200TU */
/* Save counters values for next call. */
calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp);
calib->fa_ofdm = le32toh(stats->ofdm.fa);
if (fa > 50 * rxena) {
/* High false alarm count, decrease sensitivity. */
DPRINTF(sc, IWN_DEBUG_CALIBRATE,
@ -5709,10 +5734,6 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats)
fa += le32toh(stats->cck.fa) - calib->fa_cck;
fa *= 200 * IEEE80211_DUR_TU; /* 200TU */
/* Save counters values for next call. */
calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp);
calib->fa_cck = le32toh(stats->cck.fa);
if (fa > 50 * rxena) {
/* High false alarm count, decrease sensitivity. */
DPRINTF(sc, IWN_DEBUG_CALIBRATE,
@ -5817,6 +5838,86 @@ iwn_send_sensitivity(struct iwn_softc *sc)
return iwn_cmd(sc, IWN_CMD_SET_SENSITIVITY, &cmd, len, 1);
}
/*
* Look at the increase of PLCP errors over time; if it exceeds
* a programmed threshold then trigger an RF retune.
*/
static void
iwn_check_rx_recovery(struct iwn_softc *sc, struct iwn_stats *rs)
{
int32_t delta_ofdm, delta_ht, delta_cck;
struct iwn_calib_state *calib = &sc->calib;
int delta_ticks, cur_ticks;
int delta_msec;
int thresh;
/*
* Calculate the difference between the current and
* previous statistics.
*/
delta_cck = le32toh(rs->rx.cck.bad_plcp) - calib->bad_plcp_cck;
delta_ofdm = le32toh(rs->rx.ofdm.bad_plcp) - calib->bad_plcp_ofdm;
delta_ht = le32toh(rs->rx.ht.bad_plcp) - calib->bad_plcp_ht;
/*
* Calculate the delta in time between successive statistics
* messages. Yes, it can roll over; so we make sure that
* this doesn't happen.
*
* XXX go figure out what to do about rollover
* XXX go figure out what to do if ticks rolls over to -ve instead!
* XXX go stab signed integer overflow undefined-ness in the face.
*/
cur_ticks = ticks;
delta_ticks = cur_ticks - sc->last_calib_ticks;
/*
* If any are negative, then the firmware likely reset; so just
* bail. We'll pick this up next time.
*/
if (delta_cck < 0 || delta_ofdm < 0 || delta_ht < 0 || delta_ticks < 0)
return;
/*
* delta_ticks is in ticks; we need to convert it up to milliseconds
* so we can do some useful math with it.
*/
delta_msec = ticks_to_msecs(delta_ticks);
/*
* Calculate what our threshold is given the current delta_msec.
*/
thresh = sc->base_params->plcp_err_threshold * delta_msec;
DPRINTF(sc, IWN_DEBUG_STATE,
"%s: time delta: %d; cck=%d, ofdm=%d, ht=%d, total=%d, thresh=%d\n",
__func__,
delta_msec,
delta_cck,
delta_ofdm,
delta_ht,
(delta_msec + delta_cck + delta_ofdm + delta_ht),
thresh);
/*
* If we need a retune, then schedule a single channel scan
* to a channel that isn't the currently active one!
*
* The math from linux iwlwifi:
*
* if ((delta * 100 / msecs) > threshold)
*/
if (thresh > 0 && (delta_cck + delta_ofdm + delta_ht) * 100 > thresh) {
device_printf(sc->sc_dev,
"%s: PLCP error threshold raw (%d) comparison (%d) "
"over limit (%d); retune!\n",
__func__,
(delta_cck + delta_ofdm + delta_ht),
(delta_cck + delta_ofdm + delta_ht) * 100,
thresh);
}
}
/*
* Set STA mode power saving level (between 0 and 5).
* Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving.
@ -7048,10 +7149,10 @@ iwn5000_query_calibration(struct iwn_softc *sc)
int error;
memset(&cmd, 0, sizeof cmd);
cmd.ucode.once.enable = 0xffffffff;
cmd.ucode.once.start = 0xffffffff;
cmd.ucode.once.send = 0xffffffff;
cmd.ucode.flags = 0xffffffff;
cmd.ucode.once.enable = htole32(0xffffffff);
cmd.ucode.once.start = htole32(0xffffffff);
cmd.ucode.once.send = htole32(0xffffffff);
cmd.ucode.flags = htole32(0xffffffff);
DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n",
__func__);
error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0);
@ -7107,7 +7208,7 @@ iwn5000_send_wimax_coex(struct iwn_softc *sc)
{
struct iwn5000_wimax_coex wimax;
#ifdef notyet
#if 0
if (sc->hw_type == IWN_HW_REV_TYPE_6050) {
/* Enable WiMAX coexistence for combo adapters. */
wimax.flags =

View File

@ -163,6 +163,7 @@ struct iwn_calib_state {
uint32_t bad_plcp_cck;
uint32_t fa_cck;
uint32_t low_fa;
uint32_t bad_plcp_ht;
uint8_t cck_state;
#define IWN_CCK_STATE_INIT 0
#define IWN_CCK_STATE_LOFA 1
@ -310,9 +311,11 @@ struct iwn_softc {
struct task sc_radioon_task;
struct task sc_radiooff_task;
/* Calibration information */
struct callout calib_to;
int calib_cnt;
struct iwn_calib_state calib;
int last_calib_ticks;
struct callout watchdog_to;
struct callout ct_kill_exit_to;
struct iwn_fw_info fw;