/* * Do periodic processing. This routine is called from the * driver's rx interrupt handler after processing frames. */ void ar5212AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params; int32_t listenTime; /* XXX can aniState be null? */ if (aniState == AH_NULL) return; if (!ANI_ENA(ah)) return; listenTime = ar5212AniGetListenTime(ah); if (listenTime < 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listenTime is invalid */ ar5212AniRestart(ah, aniState); } /* XXX beware of overflow? */ aniState->listenTime += listenTime; OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); params = aniState->params; if (aniState->listenTime > 5*params->period) { /* * Check to see if need to lower immunity if * 5 aniPeriods have passed */ if (ahp->ah_hasHwPhyCounters) updateMIBStats(ah, aniState); if (aniState->ofdmPhyErrCount <= aniState->listenTime * params->ofdmTrigLow/1000 && aniState->cckPhyErrCount <= aniState->listenTime * params->cckTrigLow/1000) ar5212AniLowerImmunity(ah); ar5212AniRestart(ah, aniState); } else if (aniState->listenTime > params->period) { if (ahp->ah_hasHwPhyCounters) updateMIBStats(ah, aniState); /* check to see if need to raise immunity */ if (aniState->ofdmPhyErrCount > aniState->listenTime * params->ofdmTrigHigh / 1000) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: OFDM err %u listenTime %u\n", __func__, aniState->ofdmPhyErrCount, aniState->listenTime); ar5212AniOfdmErrTrigger(ah); ar5212AniRestart(ah, aniState); } else if (aniState->cckPhyErrCount > aniState->listenTime * params->cckTrigHigh / 1000) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: CCK err %u listenTime %u\n", __func__, aniState->cckPhyErrCount, aniState->listenTime); ar5212AniCckErrTrigger(ah); ar5212AniRestart(ah, aniState); } } }
static void ar5212AniCckErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; /* first, raise noise immunity level, up to max */ aniState = ahp->ah_curani; params = aniState->params; if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__, aniState->noiseImmunityLevel + 1); ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1); return; } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrLow) { /* * Beacon signal in mid and high range, * raise firstep level. */ if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d raise ST %u\n", __func__, rssi, aniState->firstepLevel+1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } } else { /* * Beacon rssi is low, zero firstep level to maximize * CCK sensitivity in 11b/g mode. */ /* XXX can optimize */ if (IEEE80211_IS_CHAN_B(chan) || IEEE80211_IS_CHAN_G(chan)) { if (aniState->firstepLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d zero ST (was %u)\n", __func__, rssi, aniState->firstepLevel); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); } } } } }
/* * Do periodic processing. This routine is called from the * driver's rx interrupt handler after processing frames. */ void ar5416AniPoll(struct ath_hal *ah, const HAL_NODE_STATS *stats, HAL_CHANNEL *chan) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params; int32_t listenTime; ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; /* XXX can aniState be null? */ if (aniState == AH_NULL) return; if (!ANI_ENA(ah)) return; listenTime = ar5416AniGetListenTime(ah); if (listenTime < 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listenTime is invalid */ ar5416AniRestart(ah, aniState); } /* XXX beware of overflow? */ aniState->listenTime += listenTime; OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); params = aniState->params; if (aniState->listenTime > 5*params->period) { /* * Check to see if need to lower immunity if * 5 aniPeriods have passed */ updateMIBStats(ah, aniState); if (aniState->ofdmPhyErrCount <= aniState->listenTime * params->ofdmTrigLow/1000 && aniState->cckPhyErrCount <= aniState->listenTime * params->cckTrigLow/1000) ar5416AniLowerImmunity(ah); ar5416AniRestart(ah, aniState); } else if (aniState->listenTime > params->period) { updateMIBStats(ah, aniState); /* check to see if need to raise immunity */ if (aniState->ofdmPhyErrCount > aniState->listenTime * params->ofdmTrigHigh / 1000) { ar5416AniOfdmErrTrigger(ah); ar5416AniRestart(ah, aniState); } else if (aniState->cckPhyErrCount > aniState->listenTime * params->cckTrigHigh / 1000) { ar5416AniCckErrTrigger(ah); ar5416AniRestart(ah, aniState); } } }
static void ar5416AniCckErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; /* first, raise noise immunity level, up to max */ aniState = ahp->ah_curani; params = aniState->params; if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL) && aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) { ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1); return; } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrLow) { /* * Beacon signal in mid and high range, * raise firstep level. */ if (aniState->firstepLevel+1 < params->maxFirstepLevel) ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } else { /* * Beacon rssi is low, zero firstep level to maximize * CCK sensitivity in 11b/g mode. */ if (IEEE80211_IS_CHAN_CCK(chan)) { if (aniState->firstepLevel > 0) ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); } } } }
static void ar5416AniLowerImmunity(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(ANI_ENA(ah)); aniState = ahp->ah_curani; params = aniState->params; if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { /* * Beacon signal is high, leave ofdm weak signal * detection off or it may oscillate. Let it fall * through. */ } else if (rssi > params->rssiThrLow) { /* * Beacon rssi in mid range, turn on ofdm weak signal * detection or lower firstep level. */ if (aniState->ofdmWeakSigDetectOff) { ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); return; } if (aniState->firstepLevel > 0) { ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1); return; } } else { /* * Beacon rssi is low, reduce firstep level. */ if (aniState->firstepLevel > 0) { ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1); return; } } } /* then lower spur immunity level, down to zero */ if (aniState->spurImmunityLevel > 0) { ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel - 1); return; } /* * if all else fails, lower noise immunity level down to a min value * zero for now */ if (aniState->noiseImmunityLevel > 0) { ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel - 1); return; } }
static void ar5416AniOfdmErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; aniState = ahp->ah_curani; params = aniState->params; /* First, raise noise immunity level, up to max */ if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1); return; } /* then, raise spur immunity level, up to max */ if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) { ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel + 1); return; } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { /* * Beacon rssi is high, can turn off ofdm * weak sig detect. */ if (!aniState->ofdmWeakSigDetectOff) { ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); return; } /* * If weak sig detect is already off, as last resort, * raise firstep level */ if (aniState->firstepLevel+1 < params->maxFirstepLevel) { ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); return; } } else if (rssi > params->rssiThrLow) { /* * Beacon rssi in mid range, need ofdm weak signal * detect, but we can raise firststepLevel. */ if (aniState->ofdmWeakSigDetectOff) ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); if (aniState->firstepLevel+1 < params->maxFirstepLevel) ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); return; } else { /* * Beacon rssi is low, if in 11b/g mode, turn off ofdm * weak signal detection and zero firstepLevel to * maximize CCK sensitivity */ if (IEEE80211_IS_CHAN_CCK(chan)) { if (!aniState->ofdmWeakSigDetectOff) ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); if (aniState->firstepLevel > 0) ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); return; } } } }
/* * 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 ar5212AniGetListenTime(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = NULL; int32_t listenTime = 0; int good; HAL_SURVEY_SAMPLE hs; HAL_CHANNEL_SURVEY *cs = AH_NULL; /* * We shouldn't see ah_curchan be NULL, but just in case.. */ if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__); return (0); } cs = &ahp->ah_chansurvey; /* * Fetch the current statistics, squirrel away the current * sample, bump the sequence/sample counter. */ OS_MEMZERO(&hs, sizeof(hs)); good = ar5212GetMibCycleCounts(ah, &hs); if (cs != AH_NULL) { OS_MEMCPY(&cs->samples[cs->cur_sample], &hs, sizeof(hs)); cs->samples[cs->cur_sample].seq_num = cs->cur_seq; cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; cs->cur_seq++; } if (ANI_ENA(ah)) aniState = ahp->ah_curani; if (good == AH_FALSE) { /* * 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. */ listenTime = 0; ahp->ah_stats.ast_ani_lzero++; } else if (ANI_ENA(ah)) { /* * Only calculate and update the cycle count if we have * an ANI state. */ int32_t ccdelta = AH5212(ah)->ah_cycleCount - aniState->cycleCount; int32_t rfdelta = AH5212(ah)->ah_rxBusy - aniState->rxFrameCount; int32_t tfdelta = AH5212(ah)->ah_txBusy - aniState->txFrameCount; listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; } /* * Again, only update ANI state if we have it. */ if (ANI_ENA(ah)) { aniState->cycleCount = AH5212(ah)->ah_cycleCount; aniState->rxFrameCount = AH5212(ah)->ah_rxBusy; aniState->txFrameCount = AH5212(ah)->ah_txBusy; } return listenTime; }
/* * Restore/reset the ANI parameters and reset the statistics. * This routine must be called for every channel change. */ void ar5212AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_OPMODE opmode, int restore) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); /* XXX bounds check ic_devdata */ struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; uint32_t rxfilter; if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { OS_MEMZERO(aniState, sizeof(*aniState)); if (IEEE80211_IS_CHAN_2GHZ(chan)) aniState->params = &ahp->ah_aniParams24; else aniState->params = &ahp->ah_aniParams5; ichan->privFlags |= CHANNEL_ANI_INIT; HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); } ahp->ah_curani = aniState; #if 0 ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", __func__, chan->ic_freq, chan->ic_flags, restore, opmode, ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); #else HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", __func__, chan->ic_freq, chan->ic_flags, restore, opmode, ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); #endif OS_MARK(ah, AH_MARK_ANI_RESET, opmode); /* * Turn off PHY error frame delivery while we futz with settings. */ rxfilter = ah->ah_getRxFilter(ah); ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); /* * If ANI is disabled at this point, don't set the default * ANI parameter settings - leave the HAL settings there. * This is (currently) needed for reliable radar detection. */ if (! ANI_ENA(ah)) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n", __func__); goto finish; } /* * Automatic processing is done only in station mode right now. */ if (opmode == HAL_M_STA) ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; else ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; /* * Set all ani parameters. We either set them to initial * values or restore the previous ones for the channel. * XXX if ANI follows hardware, we don't care what mode we're * XXX in, we should keep the ani parameters */ if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, !aniState->ofdmWeakSigDetectOff); ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, aniState->cckWeakSigThreshold); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel); } else { ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); ichan->privFlags |= CHANNEL_ANI_SETUP; } /* * In case the counters haven't yet been setup; set them up. */ enableAniMIBCounters(ah, ahp->ah_curani->params); ar5212AniRestart(ah, aniState); finish: /* restore RX filter mask */ ah->ah_setRxFilter(ah, rxfilter); }
static void ar5212AniOfdmErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; aniState = ahp->ah_curani; params = aniState->params; /* First, raise noise immunity level, up to max */ if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__, aniState->noiseImmunityLevel + 1); ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1); return; } /* then, raise spur immunity level, up to max */ if (aniState->spurImmunityLevel+1 <= params->maxSpurImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise SI to %u\n", __func__, aniState->spurImmunityLevel + 1); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel + 1); return; } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { /* * Beacon rssi is high, can turn off ofdm * weak sig detect. */ if (!aniState->ofdmWeakSigDetectOff) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d OWSD off\n", __func__, rssi); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); return; } /* * If weak sig detect is already off, as last resort, * raise firstep level */ if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d raise ST %u\n", __func__, rssi, aniState->firstepLevel+1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); return; } } else if (rssi > params->rssiThrLow) { /* * Beacon rssi in mid range, need ofdm weak signal * detect, but we can raise firststepLevel. */ if (aniState->ofdmWeakSigDetectOff) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d OWSD on\n", __func__, rssi); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); } if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d raise ST %u\n", __func__, rssi, aniState->firstepLevel+1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } return; } else { /* * Beacon rssi is low, if in 11b/g mode, turn off ofdm * weak signal detection and zero firstepLevel to * maximize CCK sensitivity */ /* XXX can optimize */ if (IS_CHAN_B(chan) || IS_CHAN_G(chan)) { if (!aniState->ofdmWeakSigDetectOff) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d OWSD off\n", __func__, rssi); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); } if (aniState->firstepLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d zero ST (was %u)\n", __func__, rssi, aniState->firstepLevel); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); } return; } } } }