/* * Do periodic processing. This routine is called from a timer */ void ar9300_ani_ar_poll(struct ath_hal *ah, const HAL_NODE_STATS *stats, const struct ieee80211_channel *chan, HAL_ANISTATS *ani_stats) { struct ath_hal_9300 *ahp = AH9300(ah); struct ar9300_ani_state *ani_state; int32_t listen_time; u_int32_t ofdm_phy_err_rate, cck_phy_err_rate; u_int32_t ofdm_phy_err_cnt, cck_phy_err_cnt; HAL_BOOL old_phy_noise_spur; ani_state = ahp->ah_curani; ahp->ah_stats.ast_nodestats = *stats; /* XXX optimize? */ if (ani_state == NULL) { /* should not happen */ HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: can't poll - no ANI not initialized for this channel\n", __func__); return; } /* * ar9300_ani_ar_poll is never called while scanning but we may have been * scanning and now just restarted polling. In this case we need to * restore historical values. */ if (ani_state->must_restore) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: must restore - calling ar9300_ani_restart\n", __func__); ar9300_ani_reset(ah, AH_FALSE); return; } listen_time = ar9300_ani_get_listen_time(ah, ani_stats); if (listen_time <= 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listen_time is invalid */ HALDEBUG(ah, HAL_DEBUG_ANI, "%s: listen_time=%d - calling ar9300_ani_restart\n", __func__, listen_time); ar9300_ani_restart(ah); return; } /* XXX beware of overflow? */ ani_state->listen_time += listen_time; /* Clear the mib counters and save them in the stats */ ar9300_update_mib_mac_stats(ah); /* NB: these are not reset-on-read */ ofdm_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_1); cck_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_2); /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */ ahp->ah_stats.ast_ani_ofdmerrs += ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count; ani_state->ofdm_phy_err_count = ofdm_phy_err_cnt; ahp->ah_stats.ast_ani_cckerrs += cck_phy_err_cnt - ani_state->cck_phy_err_count; ani_state->cck_phy_err_count = cck_phy_err_cnt; #if HAL_ANI_DEBUG HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Errors: OFDM=0x%08x-0x0=%d CCK=0x%08x-0x0=%d\n", __func__, ofdm_phy_err_cnt, ofdm_phy_err_cnt, cck_phy_err_cnt, cck_phy_err_cnt); #endif /* * If ani is not enabled, return after we've collected * statistics */ if (!DO_ANI(ah)) { return; } ofdm_phy_err_rate = ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time; cck_phy_err_rate = ani_state->cck_phy_err_count * 1000 / ani_state->listen_time; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: listen_time=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n", __func__, listen_time, ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate, ani_state->cck_noise_immunity_level, cck_phy_err_rate, ani_state->ofdms_turn); if (ani_state->listen_time >= HAL_NOISE_DETECT_PERIOD) { old_phy_noise_spur = ani_state->phy_noise_spur; if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low && cck_phy_err_rate <= ani_state->cck_trig_low) { if (ani_state->listen_time >= HAL_NOISE_RECOVER_PERIOD) { ani_state->phy_noise_spur = 0; } } else { ani_state->phy_noise_spur = 1; } if (old_phy_noise_spur != ani_state->phy_noise_spur) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: enviroment change from %d to %d\n", __func__, old_phy_noise_spur, ani_state->phy_noise_spur); } } if (ani_state->listen_time > 5 * ahp->ah_ani_period) { /* * Check to see if need to lower immunity if * 5 ani_periods have passed */ if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low && cck_phy_err_rate <= ani_state->cck_trig_low) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: 1. listen_time=%d OFDM:%d errs=%d/s(<%d) " "CCK:%d errs=%d/s(<%d) -> ar9300_ani_lower_immunity\n", __func__, ani_state->listen_time, ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate, ani_state->ofdm_trig_low, ani_state->cck_noise_immunity_level, cck_phy_err_rate, ani_state->cck_trig_low); ar9300_ani_lower_immunity(ah); ani_state->ofdms_turn = !ani_state->ofdms_turn; } HALDEBUG(ah, HAL_DEBUG_ANI, "%s: 1 listen_time=%d ofdm=%d/s cck=%d/s - " "calling ar9300_ani_restart\n", __func__, ani_state->listen_time, ofdm_phy_err_rate, cck_phy_err_rate); ar9300_ani_restart(ah); } else if (ani_state->listen_time > ahp->ah_ani_period) { /* check to see if need to raise immunity */ if (ofdm_phy_err_rate > ani_state->ofdm_trig_high && (cck_phy_err_rate <= ani_state->cck_trig_high || ani_state->ofdms_turn)) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: 2 listen_time=%d OFDM:%d errs=%d/s(>%d) -> " "ar9300_ani_ofdm_err_trigger\n", __func__, ani_state->listen_time, ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate, ani_state->ofdm_trig_high); ar9300_ani_ofdm_err_trigger(ah); ar9300_ani_restart(ah); ani_state->ofdms_turn = AH_FALSE; } else if (cck_phy_err_rate > ani_state->cck_trig_high) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: 3 listen_time=%d CCK:%d errs=%d/s(>%d) -> " "ar9300_ani_cck_err_trigger\n", __func__, ani_state->listen_time, ani_state->cck_noise_immunity_level, cck_phy_err_rate, ani_state->cck_trig_high); ar9300_ani_cck_err_trigger(ah); ar9300_ani_restart(ah); ani_state->ofdms_turn = AH_TRUE; } } }
/* * Return an approximation of the time spent ``listening'' by * deducting the cycles spent tx'ing and rx'ing from the total * cycle count since our last call. A return value <0 indicates * an invalid/inconsistent time. */ static int32_t ar9300_ani_get_listen_time(struct ath_hal *ah, HAL_ANISTATS *ani_stats) { struct ath_hal_9300 *ahp = AH9300(ah); struct ar9300_ani_state *ani_state; u_int32_t tx_frame_count, rx_frame_count, cycle_count; int32_t listen_time; tx_frame_count = OS_REG_READ(ah, AR_TFCNT); rx_frame_count = OS_REG_READ(ah, AR_RFCNT); cycle_count = OS_REG_READ(ah, AR_CCCNT); ani_state = ahp->ah_curani; #if ATH_SUPPORT_VOW_DCS if (ani_state->cycle_count == 0 || ani_state->cycle_count > cycle_count || ani_state->tx_frame_count > tx_frame_count || ani_state->rx_frame_count > rx_frame_count) { #else if (ani_state->cycle_count == 0 || ani_state->cycle_count > cycle_count) { #endif /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers * right shift rather than wrap--so punt and return 0. */ listen_time = 0; ahp->ah_stats.ast_ani_lzero++; #if HAL_ANI_DEBUG HDPRINTF(ah, HAL_DBG_ANI, "%s: 1st call: ani_state->cycle_count=%d\n", __func__, ani_state->cycle_count); #endif } else { int32_t ccdelta = cycle_count - ani_state->cycle_count; int32_t rfdelta = rx_frame_count - ani_state->rx_frame_count; int32_t tfdelta = tx_frame_count - ani_state->tx_frame_count; listen_time = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE(ah); #if ATH_SUPPORT_VOW_DCS ani_stats->cyclecnt_diff = ccdelta; ani_stats->txframecnt_diff = tfdelta; ani_stats->rxframecnt_diff = rfdelta; ani_stats->valid = AH_TRUE; #endif #if HAL_ANI_DEBUG HDPRINTF(ah, HAL_DBG_ANI, "%s: cyclecount=%d, rfcount=%d, tfcount=%d, listen_time=%d " "CLOCK_RATE=%d\n", __func__, ccdelta, rfdelta, tfdelta, listen_time, CLOCK_RATE(ah)); #endif } #if ATH_SUPPORT_VOW_DCS ani_stats->rxclr_cnt = OS_REG_READ(ah, AR_RCCNT); #endif ani_state->cycle_count = cycle_count; ani_state->tx_frame_count = tx_frame_count; ani_state->rx_frame_count = rx_frame_count; return listen_time; } /* * Do periodic processing. This routine is called from a timer */ void ar9300_ani_ar_poll(struct ath_hal *ah, const HAL_NODE_STATS *stats, HAL_CHANNEL *chan, HAL_ANISTATS *ani_stats) { struct ath_hal_9300 *ahp = AH9300(ah); struct ar9300_ani_state *ani_state; int32_t listen_time; u_int32_t ofdm_phy_err_rate, cck_phy_err_rate; u_int32_t ofdm_phy_err_cnt, cck_phy_err_cnt; bool old_phy_noise_spur; ani_state = ahp->ah_curani; ahp->ah_stats.ast_nodestats = *stats; /* XXX optimize? */ if (ani_state == NULL) { /* should not happen */ HDPRINTF(ah, HAL_DBG_UNMASKABLE, "%s: can't poll - no ANI not initialized for this channel\n", __func__); #if ATH_SUPPORT_VOW_DCS ani_stats->valid = false; #endif return; } /* * ar9300_ani_ar_poll is never called while scanning but we may have been * scanning and now just restarted polling. In this case we need to * restore historical values. */ if (ani_state->must_restore) { HDPRINTF(ah, HAL_DBG_ANI, "%s: must restore - calling ar9300_ani_restart\n", __func__); ar9300_ani_reset(ah, false); #if ATH_SUPPORT_VOW_DCS ani_stats->valid = false; #endif return; } listen_time = ar9300_ani_get_listen_time(ah, ani_stats); if (listen_time <= 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listen_time is invalid */ HDPRINTF(ah, HAL_DBG_ANI, "%s: listen_time=%d - calling ar9300_ani_restart\n", __func__, listen_time); ar9300_ani_restart(ah); #if ATH_SUPPORT_VOW_DCS ani_stats->valid = false; #endif return; } /* XXX beware of overflow? */ ani_state->listen_time += listen_time; /* Clear the mib counters and save them in the stats */ ar9300_update_mib_mac_stats(ah); /* NB: these are not reset-on-read */ ofdm_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_1); cck_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_2); #if ATH_SUPPORT_VOW_DCS ani_stats->listen_time = ani_state->listen_time; ani_stats->ofdmphyerr_cnt = ofdm_phy_err_cnt; ani_stats->cckphyerr_cnt = cck_phy_err_cnt; ani_stats->ofdmphyerrcnt_diff = ( ani_state->ofdm_phy_err_count <= ofdm_phy_err_cnt )? ( ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count ) : ofdm_phy_err_cnt ; #endif /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */ ahp->ah_stats.ast_ani_ofdmerrs += ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count; ani_state->ofdm_phy_err_count = ofdm_phy_err_cnt; ahp->ah_stats.ast_ani_cckerrs += cck_phy_err_cnt - ani_state->cck_phy_err_count; ani_state->cck_phy_err_count = cck_phy_err_cnt; #if HAL_ANI_DEBUG HDPRINTF(ah, HAL_DBG_ANI, "%s: Errors: OFDM=0x%08x-0x0=%d CCK=0x%08x-0x0=%d\n", __func__, ofdm_phy_err_cnt, ofdm_phy_err_cnt, cck_phy_err_cnt, cck_phy_err_cnt); #endif /* * If ani is not enabled, return after we've collected * statistics */ if (!DO_ANI(ah)) { return; } ofdm_phy_err_rate = ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time; cck_phy_err_rate = ani_state->cck_phy_err_count * 1000 / ani_state->listen_time; HDPRINTF(ah, HAL_DBG_ANI, "%s: listen_time=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n", __func__, listen_time, ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate, ani_state->cck_noise_immunity_level, cck_phy_err_rate, ani_state->ofdms_turn); if (ani_state->listen_time >= HAL_NOISE_DETECT_PERIOD) { old_phy_noise_spur = ani_state->phy_noise_spur; if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low && cck_phy_err_rate <= ani_state->cck_trig_low) { if (ani_state->listen_time >= HAL_NOISE_RECOVER_PERIOD) { ani_state->phy_noise_spur = 0; } } else { ani_state->phy_noise_spur = 1; } if (old_phy_noise_spur != ani_state->phy_noise_spur) { HDPRINTF(ah, HAL_DBG_ANI, "%s: enviroment change from %d to %d\n", __func__, old_phy_noise_spur, ani_state->phy_noise_spur); } } if (ani_state->listen_time > 5 * ahp->ah_ani_period) { /* * Check to see if need to lower immunity if * 5 ani_periods have passed */ if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low && cck_phy_err_rate <= ani_state->cck_trig_low) { HDPRINTF(ah, HAL_DBG_ANI, "%s: 1. listen_time=%d OFDM:%d errs=%d/s(<%d) " "CCK:%d errs=%d/s(<%d) -> ar9300_ani_lower_immunity\n", __func__, ani_state->listen_time, ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate, ani_state->ofdm_trig_low, ani_state->cck_noise_immunity_level, cck_phy_err_rate, ani_state->cck_trig_low); ar9300_ani_lower_immunity(ah); ani_state->ofdms_turn = !ani_state->ofdms_turn; } HDPRINTF(ah, HAL_DBG_ANI, "%s: 1 listen_time=%d ofdm=%d/s cck=%d/s - " "calling ar9300_ani_restart\n", __func__, ani_state->listen_time, ofdm_phy_err_rate, cck_phy_err_rate); ar9300_ani_restart(ah); } else if (ani_state->listen_time > ahp->ah_ani_period) { /* check to see if need to raise immunity */ if (ofdm_phy_err_rate > ani_state->ofdm_trig_high && (cck_phy_err_rate <= ani_state->cck_trig_high || ani_state->ofdms_turn)) { HDPRINTF(ah, HAL_DBG_ANI, "%s: 2 listen_time=%d OFDM:%d errs=%d/s(>%d) -> " "ar9300_ani_ofdm_err_trigger\n", __func__, ani_state->listen_time, ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate, ani_state->ofdm_trig_high); ar9300_ani_ofdm_err_trigger(ah); ar9300_ani_restart(ah); ani_state->ofdms_turn = false; } else if (cck_phy_err_rate > ani_state->cck_trig_high) { HDPRINTF(ah, HAL_DBG_ANI, "%s: 3 listen_time=%d CCK:%d errs=%d/s(>%d) -> " "ar9300_ani_cck_err_trigger\n", __func__, ani_state->listen_time, ani_state->cck_noise_immunity_level, cck_phy_err_rate, ani_state->cck_trig_high); ar9300_ani_cck_err_trigger(ah); ar9300_ani_restart(ah); ani_state->ofdms_turn = true; } } } /* * The poll function above calculates short noise spurs, caused by non-80211 * devices, based on OFDM/CCK Phy errs. * If the noise is short enough, we don't want our ratectrl Algo to stop probing * higher rates, due to bad PER. */ bool ar9300_is_ani_noise_spur(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); struct ar9300_ani_state *ani_state; ani_state = ahp->ah_curani; return ani_state->phy_noise_spur; }