ar5416_ani.c revision 219767
1185380Ssam/* 2187831Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3185380Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc. 4185380Ssam * 5185380Ssam * Permission to use, copy, modify, and/or distribute this software for any 6185380Ssam * purpose with or without fee is hereby granted, provided that the above 7185380Ssam * copyright notice and this permission notice appear in all copies. 8185380Ssam * 9185380Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10185380Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11185380Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12185380Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13185380Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14185380Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15185380Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16185380Ssam * 17187831Ssam * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 219767 2011-03-19 00:46:10Z adrian $ 18185380Ssam */ 19185380Ssam#include "opt_ah.h" 20185380Ssam 21185380Ssam/* 22185380Ssam * XXX this is virtually the same code as for 5212; we reuse 23185380Ssam * storage in the 5212 state block; need to refactor. 24185380Ssam */ 25185380Ssam#include "ah.h" 26185380Ssam#include "ah_internal.h" 27185380Ssam#include "ah_desc.h" 28185380Ssam 29185380Ssam#include "ar5416/ar5416.h" 30185380Ssam#include "ar5416/ar5416reg.h" 31185380Ssam#include "ar5416/ar5416phy.h" 32185380Ssam 33185380Ssam/* 34185380Ssam * Anti noise immunity support. We track phy errors and react 35185380Ssam * to excessive errors by adjusting the noise immunity parameters. 36185380Ssam */ 37185380Ssam 38185380Ssam#define HAL_EP_RND(x, mul) \ 39185380Ssam ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 40185380Ssam#define BEACON_RSSI(ahp) \ 41185380Ssam HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ 42185380Ssam HAL_RSSI_EP_MULTIPLIER) 43185380Ssam 44185380Ssam/* 45185380Ssam * ANI processing tunes radio parameters according to PHY errors 46185380Ssam * and related information. This is done for for noise and spur 47185380Ssam * immunity in all operating modes if the device indicates it's 48185380Ssam * capable at attach time. In addition, when there is a reference 49185380Ssam * rssi value (e.g. beacon frames from an ap in station mode) 50185380Ssam * further tuning is done. 51185380Ssam * 52185380Ssam * ANI_ENA indicates whether any ANI processing should be done; 53185380Ssam * this is specified at attach time. 54185380Ssam * 55185380Ssam * ANI_ENA_RSSI indicates whether rssi-based processing should 56185380Ssam * done, this is enabled based on operating mode and is meaningful 57185380Ssam * only if ANI_ENA is true. 58185380Ssam * 59185380Ssam * ANI parameters are typically controlled only by the hal. The 60185380Ssam * AniControl interface however permits manual tuning through the 61185380Ssam * diagnostic api. 62185380Ssam */ 63185380Ssam#define ANI_ENA(ah) \ 64185380Ssam (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) 65185380Ssam#define ANI_ENA_RSSI(ah) \ 66185380Ssam (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) 67185380Ssam 68185380Ssam#define ah_mibStats ah_stats.ast_mibstats 69185380Ssam 70185380Ssamstatic void 71185380SsamenableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) 72185380Ssam{ 73185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 74185380Ssam 75185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " 76185380Ssam "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n", 77185380Ssam __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); 78185380Ssam 79185380Ssam OS_REG_WRITE(ah, AR_FILTOFDM, 0); 80185380Ssam OS_REG_WRITE(ah, AR_FILTCCK, 0); 81185380Ssam 82185380Ssam OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); 83185380Ssam OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); 84185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 85185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); 86185380Ssam 87185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ 88185380Ssam ar5212EnableMibCounters(ah); /* enable everything */ 89185380Ssam} 90185380Ssam 91185380Ssamstatic void 92185380SsamdisableAniMIBCounters(struct ath_hal *ah) 93185380Ssam{ 94185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 95185380Ssam 96185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n"); 97185380Ssam 98185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ 99185380Ssam ar5212DisableMibCounters(ah); /* disable everything */ 100185380Ssam 101185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0); 102185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0); 103185380Ssam} 104185380Ssam 105185380Ssamstatic void 106185380SsamsetPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) 107185380Ssam{ 108185380Ssam if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { 109185380Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 110185380Ssam "OFDM Trigger %d is too high for hw counters, using max\n", 111185380Ssam params->ofdmTrigHigh); 112185380Ssam params->ofdmPhyErrBase = 0; 113185380Ssam } else 114185380Ssam params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; 115185380Ssam if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { 116185380Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 117185380Ssam "CCK Trigger %d is too high for hw counters, using max\n", 118185380Ssam params->cckTrigHigh); 119185380Ssam params->cckPhyErrBase = 0; 120185380Ssam } else 121185380Ssam params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; 122185380Ssam} 123185380Ssam 124185380Ssam/* 125185380Ssam * Setup ANI handling. Sets all thresholds and reset the 126185380Ssam * channel statistics. Note that ar5416AniReset should be 127185380Ssam * called by ar5416Reset before anything else happens and 128185380Ssam * that's where we force initial settings. 129185380Ssam */ 130185380Ssamvoid 131185380Ssamar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, 132185380Ssam const struct ar5212AniParams *params5, HAL_BOOL enable) 133185380Ssam{ 134185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 135185380Ssam 136185380Ssam if (params24 != AH_NULL) { 137185380Ssam OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); 138185380Ssam setPhyErrBase(ah, &ahp->ah_aniParams24); 139185380Ssam } 140185380Ssam if (params5 != AH_NULL) { 141185380Ssam OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); 142185380Ssam setPhyErrBase(ah, &ahp->ah_aniParams5); 143185380Ssam } 144185380Ssam 145185380Ssam OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); 146185380Ssam /* Enable MIB Counters */ 147185380Ssam enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); 148185380Ssam 149185380Ssam if (enable) { /* Enable ani now */ 150185380Ssam HALASSERT(params24 != AH_NULL && params5 != AH_NULL); 151185380Ssam ahp->ah_procPhyErr |= HAL_ANI_ENA; 152185380Ssam } else { 153185380Ssam ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 154185380Ssam } 155185380Ssam} 156185380Ssam 157185380Ssam/* 158185380Ssam * Cleanup any ANI state setup. 159185380Ssam */ 160185380Ssamvoid 161185380Ssamar5416AniDetach(struct ath_hal *ah) 162185380Ssam{ 163185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n"); 164185380Ssam disableAniMIBCounters(ah); 165185380Ssam} 166185380Ssam 167185380Ssam/* 168185380Ssam * Control Adaptive Noise Immunity Parameters 169185380Ssam */ 170185380SsamHAL_BOOL 171185380Ssamar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) 172185380Ssam{ 173185380Ssam typedef int TABLE[]; 174185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 175185380Ssam struct ar5212AniState *aniState = ahp->ah_curani; 176185380Ssam const struct ar5212AniParams *params = aniState->params; 177185380Ssam 178185380Ssam OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); 179185380Ssam 180218763Sadrian switch (cmd & AH5416(ah)->ah_ani_function) { 181185380Ssam case HAL_ANI_NOISE_IMMUNITY_LEVEL: { 182185380Ssam u_int level = param; 183185380Ssam 184217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level); 185185380Ssam if (level >= params->maxNoiseImmunityLevel) { 186217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 187203159Srpaulo "%s: immunity level out of range (%u > %u)\n", 188185380Ssam __func__, level, params->maxNoiseImmunityLevel); 189185380Ssam return AH_FALSE; 190185380Ssam } 191185380Ssam 192185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, 193185380Ssam AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); 194185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 195185380Ssam AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); 196185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 197185380Ssam AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); 198185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 199185380Ssam AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); 200185380Ssam 201185380Ssam if (level > aniState->noiseImmunityLevel) 202185380Ssam ahp->ah_stats.ast_ani_niup++; 203185380Ssam else if (level < aniState->noiseImmunityLevel) 204185380Ssam ahp->ah_stats.ast_ani_nidown++; 205185380Ssam aniState->noiseImmunityLevel = level; 206185380Ssam break; 207185380Ssam } 208185380Ssam case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { 209185380Ssam static const TABLE m1ThreshLow = { 127, 50 }; 210185380Ssam static const TABLE m2ThreshLow = { 127, 40 }; 211185380Ssam static const TABLE m1Thresh = { 127, 0x4d }; 212185380Ssam static const TABLE m2Thresh = { 127, 0x40 }; 213185380Ssam static const TABLE m2CountThr = { 31, 16 }; 214185380Ssam static const TABLE m2CountThrLow = { 63, 48 }; 215185380Ssam u_int on = param ? 1 : 0; 216185380Ssam 217217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled"); 218185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 219185380Ssam AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); 220185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 221185380Ssam AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); 222185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 223185380Ssam AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); 224185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 225185380Ssam AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); 226185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 227185380Ssam AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); 228185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 229185380Ssam AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); 230185380Ssam 231185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 232185380Ssam AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]); 233185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 234185380Ssam AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]); 235185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 236185380Ssam AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]); 237185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 238185380Ssam AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]); 239185380Ssam 240185380Ssam if (on) { 241185380Ssam OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, 242185380Ssam AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 243185380Ssam } else { 244185380Ssam OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, 245185380Ssam AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 246185380Ssam } 247185380Ssam if (on) 248185380Ssam ahp->ah_stats.ast_ani_ofdmon++; 249185380Ssam else 250185380Ssam ahp->ah_stats.ast_ani_ofdmoff++; 251185380Ssam aniState->ofdmWeakSigDetectOff = !on; 252185380Ssam break; 253185380Ssam } 254185380Ssam case HAL_ANI_CCK_WEAK_SIGNAL_THR: { 255185380Ssam static const TABLE weakSigThrCck = { 8, 6 }; 256185380Ssam u_int high = param ? 1 : 0; 257185380Ssam 258217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low"); 259185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, 260185380Ssam AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); 261185380Ssam if (high) 262185380Ssam ahp->ah_stats.ast_ani_cckhigh++; 263185380Ssam else 264185380Ssam ahp->ah_stats.ast_ani_ccklow++; 265185380Ssam aniState->cckWeakSigThreshold = high; 266185380Ssam break; 267185380Ssam } 268185380Ssam case HAL_ANI_FIRSTEP_LEVEL: { 269185380Ssam u_int level = param; 270185380Ssam 271217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level); 272185380Ssam if (level >= params->maxFirstepLevel) { 273217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 274203159Srpaulo "%s: firstep level out of range (%u > %u)\n", 275185380Ssam __func__, level, params->maxFirstepLevel); 276185380Ssam return AH_FALSE; 277185380Ssam } 278185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 279185380Ssam AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); 280185380Ssam if (level > aniState->firstepLevel) 281185380Ssam ahp->ah_stats.ast_ani_stepup++; 282185380Ssam else if (level < aniState->firstepLevel) 283185380Ssam ahp->ah_stats.ast_ani_stepdown++; 284185380Ssam aniState->firstepLevel = level; 285185380Ssam break; 286185380Ssam } 287185380Ssam case HAL_ANI_SPUR_IMMUNITY_LEVEL: { 288185380Ssam u_int level = param; 289185380Ssam 290217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level); 291185380Ssam if (level >= params->maxSpurImmunityLevel) { 292217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 293203159Srpaulo "%s: spur immunity level out of range (%u > %u)\n", 294185380Ssam __func__, level, params->maxSpurImmunityLevel); 295185380Ssam return AH_FALSE; 296185380Ssam } 297185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, 298185380Ssam AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); 299185380Ssam if (level > aniState->spurImmunityLevel) 300185380Ssam ahp->ah_stats.ast_ani_spurup++; 301185380Ssam else if (level < aniState->spurImmunityLevel) 302185380Ssam ahp->ah_stats.ast_ani_spurdown++; 303185380Ssam aniState->spurImmunityLevel = level; 304185380Ssam break; 305185380Ssam } 306185380Ssam case HAL_ANI_PRESENT: 307185380Ssam break; 308185380Ssam case HAL_ANI_MODE: 309185380Ssam if (param == 0) { 310185380Ssam ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 311185380Ssam /* Turn off HW counters if we have them */ 312185380Ssam ar5416AniDetach(ah); 313185380Ssam ar5212SetRxFilter(ah, 314185380Ssam ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 315185380Ssam } else { /* normal/auto mode */ 316185380Ssam /* don't mess with state if already enabled */ 317185380Ssam if (ahp->ah_procPhyErr & HAL_ANI_ENA) 318185380Ssam break; 319185380Ssam ar5212SetRxFilter(ah, 320185380Ssam ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); 321185380Ssam /* Enable MIB Counters */ 322185380Ssam enableAniMIBCounters(ah, ahp->ah_curani != AH_NULL ? 323185380Ssam ahp->ah_curani->params: &ahp->ah_aniParams24 /*XXX*/); 324185380Ssam ahp->ah_procPhyErr |= HAL_ANI_ENA; 325185380Ssam } 326185380Ssam break; 327185380Ssam#ifdef AH_PRIVATE_DIAG 328185380Ssam case HAL_ANI_PHYERR_RESET: 329185380Ssam ahp->ah_stats.ast_ani_ofdmerrs = 0; 330185380Ssam ahp->ah_stats.ast_ani_cckerrs = 0; 331185380Ssam break; 332185380Ssam#endif /* AH_PRIVATE_DIAG */ 333185380Ssam default: 334217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n", 335185380Ssam __func__, cmd); 336185380Ssam return AH_FALSE; 337185380Ssam } 338185380Ssam return AH_TRUE; 339185380Ssam} 340185380Ssam 341185380Ssamstatic void 342185380Ssamar5416AniOfdmErrTrigger(struct ath_hal *ah) 343185380Ssam{ 344185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 345187831Ssam const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 346185380Ssam struct ar5212AniState *aniState; 347185380Ssam const struct ar5212AniParams *params; 348185380Ssam 349185380Ssam HALASSERT(chan != AH_NULL); 350185380Ssam 351185380Ssam if (!ANI_ENA(ah)) 352185380Ssam return; 353185380Ssam 354185380Ssam aniState = ahp->ah_curani; 355185380Ssam params = aniState->params; 356185380Ssam /* First, raise noise immunity level, up to max */ 357218763Sadrian if ((AH5416(ah)->ah_ani_function & HAL_ANI_NOISE_IMMUNITY_LEVEL) && 358218763Sadrian (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) { 359185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 360185380Ssam aniState->noiseImmunityLevel + 1); 361185380Ssam return; 362185380Ssam } 363185380Ssam /* then, raise spur immunity level, up to max */ 364218763Sadrian if ((AH5416(ah)->ah_ani_function & HAL_ANI_SPUR_IMMUNITY_LEVEL) && 365218763Sadrian (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel)) { 366185380Ssam ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 367185380Ssam aniState->spurImmunityLevel + 1); 368185380Ssam return; 369185380Ssam } 370185380Ssam 371185380Ssam if (ANI_ENA_RSSI(ah)) { 372185380Ssam int32_t rssi = BEACON_RSSI(ahp); 373185380Ssam if (rssi > params->rssiThrHigh) { 374185380Ssam /* 375185380Ssam * Beacon rssi is high, can turn off ofdm 376185380Ssam * weak sig detect. 377185380Ssam */ 378185380Ssam if (!aniState->ofdmWeakSigDetectOff) { 379185380Ssam ar5416AniControl(ah, 380185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 381185380Ssam AH_FALSE); 382185380Ssam ar5416AniControl(ah, 383185380Ssam HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 384185380Ssam return; 385185380Ssam } 386185380Ssam /* 387185380Ssam * If weak sig detect is already off, as last resort, 388185380Ssam * raise firstep level 389185380Ssam */ 390185380Ssam if (aniState->firstepLevel+1 < params->maxFirstepLevel) { 391185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 392185380Ssam aniState->firstepLevel + 1); 393185380Ssam return; 394185380Ssam } 395185380Ssam } else if (rssi > params->rssiThrLow) { 396185380Ssam /* 397185380Ssam * Beacon rssi in mid range, need ofdm weak signal 398185380Ssam * detect, but we can raise firststepLevel. 399185380Ssam */ 400185380Ssam if (aniState->ofdmWeakSigDetectOff) 401185380Ssam ar5416AniControl(ah, 402185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 403185380Ssam AH_TRUE); 404185380Ssam if (aniState->firstepLevel+1 < params->maxFirstepLevel) 405185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 406185380Ssam aniState->firstepLevel + 1); 407185380Ssam return; 408185380Ssam } else { 409185380Ssam /* 410185380Ssam * Beacon rssi is low, if in 11b/g mode, turn off ofdm 411185380Ssam * weak signal detection and zero firstepLevel to 412185380Ssam * maximize CCK sensitivity 413185380Ssam */ 414187831Ssam if (IEEE80211_IS_CHAN_CCK(chan)) { 415185380Ssam if (!aniState->ofdmWeakSigDetectOff) 416185380Ssam ar5416AniControl(ah, 417185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 418185380Ssam AH_FALSE); 419185380Ssam if (aniState->firstepLevel > 0) 420185380Ssam ar5416AniControl(ah, 421185380Ssam HAL_ANI_FIRSTEP_LEVEL, 0); 422185380Ssam return; 423185380Ssam } 424185380Ssam } 425185380Ssam } 426185380Ssam} 427185380Ssam 428185380Ssamstatic void 429185380Ssamar5416AniCckErrTrigger(struct ath_hal *ah) 430185380Ssam{ 431185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 432187831Ssam const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 433185380Ssam struct ar5212AniState *aniState; 434185380Ssam const struct ar5212AniParams *params; 435185380Ssam 436185380Ssam HALASSERT(chan != AH_NULL); 437185380Ssam 438185380Ssam if (!ANI_ENA(ah)) 439185380Ssam return; 440185380Ssam 441185380Ssam /* first, raise noise immunity level, up to max */ 442185380Ssam aniState = ahp->ah_curani; 443185380Ssam params = aniState->params; 444185380Ssam if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { 445185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 446185380Ssam aniState->noiseImmunityLevel + 1); 447185380Ssam return; 448185380Ssam } 449185380Ssam 450185380Ssam if (ANI_ENA_RSSI(ah)) { 451185380Ssam int32_t rssi = BEACON_RSSI(ahp); 452185380Ssam if (rssi > params->rssiThrLow) { 453185380Ssam /* 454185380Ssam * Beacon signal in mid and high range, 455185380Ssam * raise firstep level. 456185380Ssam */ 457185380Ssam if (aniState->firstepLevel+1 < params->maxFirstepLevel) 458185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 459185380Ssam aniState->firstepLevel + 1); 460185380Ssam } else { 461185380Ssam /* 462185380Ssam * Beacon rssi is low, zero firstep level to maximize 463185380Ssam * CCK sensitivity in 11b/g mode. 464185380Ssam */ 465187831Ssam if (IEEE80211_IS_CHAN_CCK(chan)) { 466185380Ssam if (aniState->firstepLevel > 0) 467185380Ssam ar5416AniControl(ah, 468185380Ssam HAL_ANI_FIRSTEP_LEVEL, 0); 469185380Ssam } 470185380Ssam } 471185380Ssam } 472185380Ssam} 473185380Ssam 474185380Ssamstatic void 475185380Ssamar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) 476185380Ssam{ 477185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 478185380Ssam const struct ar5212AniParams *params = aniState->params; 479185380Ssam 480185380Ssam aniState->listenTime = 0; 481185380Ssam /* 482185380Ssam * NB: these are written on reset based on the 483185380Ssam * ini so we must re-write them! 484185380Ssam */ 485185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 486185380Ssam "%s: Writing ofdmbase=%u cckbase=%u\n", __func__, 487185380Ssam params->ofdmPhyErrBase, params->cckPhyErrBase); 488185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); 489185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); 490185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 491185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_CCK_TIMING); 492185380Ssam 493185380Ssam /* Clear the mib counters and save them in the stats */ 494185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 495185380Ssam aniState->ofdmPhyErrCount = 0; 496185380Ssam aniState->cckPhyErrCount = 0; 497185380Ssam} 498185380Ssam 499185380Ssam/* 500185380Ssam * Restore/reset the ANI parameters and reset the statistics. 501185380Ssam * This routine must be called for every channel change. 502185380Ssam * 503185380Ssam * NOTE: This is where ah_curani is set; other ani code assumes 504185380Ssam * it is setup to reflect the current channel. 505185380Ssam */ 506185380Ssamvoid 507187831Ssamar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, 508185380Ssam HAL_OPMODE opmode, int restore) 509185380Ssam{ 510185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 511187831Ssam HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); 512187831Ssam /* XXX bounds check ic_devdata */ 513187831Ssam struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; 514185380Ssam uint32_t rxfilter; 515185380Ssam 516187831Ssam if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { 517187831Ssam OS_MEMZERO(aniState, sizeof(*aniState)); 518187831Ssam if (IEEE80211_IS_CHAN_2GHZ(chan)) 519187831Ssam aniState->params = &ahp->ah_aniParams24; 520187831Ssam else 521187831Ssam aniState->params = &ahp->ah_aniParams5; 522187831Ssam ichan->privFlags |= CHANNEL_ANI_INIT; 523187831Ssam HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); 524187831Ssam } 525185380Ssam ahp->ah_curani = aniState; 526185380Ssam#if 0 527187831Ssam ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", 528187831Ssam __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 529187831Ssam ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 530185380Ssam#else 531187831Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", 532187831Ssam __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 533187831Ssam ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 534185380Ssam#endif 535185380Ssam OS_MARK(ah, AH_MARK_ANI_RESET, opmode); 536185380Ssam 537185380Ssam /* 538185380Ssam * Turn off PHY error frame delivery while we futz with settings. 539185380Ssam */ 540185380Ssam rxfilter = ar5212GetRxFilter(ah); 541185380Ssam ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); 542185380Ssam /* 543185380Ssam * Automatic processing is done only in station mode right now. 544185380Ssam */ 545185380Ssam if (opmode == HAL_M_STA) 546185380Ssam ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; 547185380Ssam else 548185380Ssam ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; 549185380Ssam /* 550185380Ssam * Set all ani parameters. We either set them to initial 551185380Ssam * values or restore the previous ones for the channel. 552185380Ssam * XXX if ANI follows hardware, we don't care what mode we're 553185380Ssam * XXX in, we should keep the ani parameters 554185380Ssam */ 555187831Ssam if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { 556185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 557185380Ssam aniState->noiseImmunityLevel); 558185380Ssam ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 559185380Ssam aniState->spurImmunityLevel); 560185380Ssam ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 561185380Ssam !aniState->ofdmWeakSigDetectOff); 562185380Ssam ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, 563185380Ssam aniState->cckWeakSigThreshold); 564185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 565185380Ssam aniState->firstepLevel); 566185380Ssam } else { 567185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); 568185380Ssam ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 569185380Ssam ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 570185380Ssam AH_TRUE); 571185380Ssam ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); 572185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); 573187831Ssam ichan->privFlags |= CHANNEL_ANI_SETUP; 574185380Ssam } 575185380Ssam ar5416AniRestart(ah, aniState); 576185380Ssam 577185380Ssam /* restore RX filter mask */ 578185380Ssam ar5212SetRxFilter(ah, rxfilter); 579185380Ssam} 580185380Ssam 581185380Ssam/* 582185380Ssam * Process a MIB interrupt. We may potentially be invoked because 583185380Ssam * any of the MIB counters overflow/trigger so don't assume we're 584185380Ssam * here because a PHY error counter triggered. 585185380Ssam */ 586185380Ssamvoid 587185380Ssamar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) 588185380Ssam{ 589185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 590185380Ssam uint32_t phyCnt1, phyCnt2; 591185380Ssam 592185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " 593185380Ssam "filtofdm 0x%x filtcck 0x%x\n", 594185380Ssam __func__, OS_REG_READ(ah, AR_MIBC), 595185380Ssam OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), 596185380Ssam OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); 597185380Ssam 598185380Ssam /* 599185380Ssam * First order of business is to clear whatever caused 600185380Ssam * the interrupt so we don't keep getting interrupted. 601185380Ssam * We have the usual mib counters that are reset-on-read 602185380Ssam * and the additional counters that appeared starting in 603185380Ssam * Hainan. We collect the mib counters and explicitly 604185380Ssam * zero additional counters we are not using. Anything 605185380Ssam * else is reset only if it caused the interrupt. 606185380Ssam */ 607185380Ssam /* NB: these are not reset-on-read */ 608185380Ssam phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 609185380Ssam phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 610185380Ssam /* not used, always reset them in case they are the cause */ 611185380Ssam OS_REG_WRITE(ah, AR_FILTOFDM, 0); 612185380Ssam OS_REG_WRITE(ah, AR_FILTCCK, 0); 613185380Ssam if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) 614185380Ssam OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); 615185380Ssam 616185380Ssam /* Clear the mib counters and save them in the stats */ 617185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 618185380Ssam ahp->ah_stats.ast_nodestats = *stats; 619185380Ssam 620185380Ssam /* 621185380Ssam * Check for an ani stat hitting the trigger threshold. 622185380Ssam * When this happens we get a MIB interrupt and the top 623185380Ssam * 2 bits of the counter register will be 0b11, hence 624185380Ssam * the mask check of phyCnt?. 625185380Ssam */ 626185380Ssam if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || 627185380Ssam ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { 628185380Ssam struct ar5212AniState *aniState = ahp->ah_curani; 629185380Ssam const struct ar5212AniParams *params = aniState->params; 630185380Ssam uint32_t ofdmPhyErrCnt, cckPhyErrCnt; 631185380Ssam 632185380Ssam ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 633185380Ssam ahp->ah_stats.ast_ani_ofdmerrs += 634185380Ssam ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 635185380Ssam aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 636185380Ssam 637185380Ssam cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 638185380Ssam ahp->ah_stats.ast_ani_cckerrs += 639185380Ssam cckPhyErrCnt - aniState->cckPhyErrCount; 640185380Ssam aniState->cckPhyErrCount = cckPhyErrCnt; 641185380Ssam 642185380Ssam /* 643185380Ssam * NB: figure out which counter triggered. If both 644185380Ssam * trigger we'll only deal with one as the processing 645185380Ssam * clobbers the error counter so the trigger threshold 646185380Ssam * check will never be true. 647185380Ssam */ 648185380Ssam if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) 649185380Ssam ar5416AniOfdmErrTrigger(ah); 650185380Ssam if (aniState->cckPhyErrCount > params->cckTrigHigh) 651185380Ssam ar5416AniCckErrTrigger(ah); 652185380Ssam /* NB: always restart to insure the h/w counters are reset */ 653185380Ssam ar5416AniRestart(ah, aniState); 654185380Ssam } 655185380Ssam} 656185380Ssam 657185380Ssamstatic void 658185380Ssamar5416AniLowerImmunity(struct ath_hal *ah) 659185380Ssam{ 660185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 661185380Ssam struct ar5212AniState *aniState; 662185380Ssam const struct ar5212AniParams *params; 663185380Ssam 664185380Ssam HALASSERT(ANI_ENA(ah)); 665185380Ssam 666185380Ssam aniState = ahp->ah_curani; 667185380Ssam params = aniState->params; 668185380Ssam if (ANI_ENA_RSSI(ah)) { 669185380Ssam int32_t rssi = BEACON_RSSI(ahp); 670185380Ssam if (rssi > params->rssiThrHigh) { 671185380Ssam /* 672185380Ssam * Beacon signal is high, leave ofdm weak signal 673185380Ssam * detection off or it may oscillate. Let it fall 674185380Ssam * through. 675185380Ssam */ 676185380Ssam } else if (rssi > params->rssiThrLow) { 677185380Ssam /* 678185380Ssam * Beacon rssi in mid range, turn on ofdm weak signal 679185380Ssam * detection or lower firstep level. 680185380Ssam */ 681185380Ssam if (aniState->ofdmWeakSigDetectOff) { 682185380Ssam ar5416AniControl(ah, 683185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 684185380Ssam AH_TRUE); 685185380Ssam return; 686185380Ssam } 687185380Ssam if (aniState->firstepLevel > 0) { 688185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 689185380Ssam aniState->firstepLevel - 1); 690185380Ssam return; 691185380Ssam } 692185380Ssam } else { 693185380Ssam /* 694185380Ssam * Beacon rssi is low, reduce firstep level. 695185380Ssam */ 696185380Ssam if (aniState->firstepLevel > 0) { 697185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 698185380Ssam aniState->firstepLevel - 1); 699185380Ssam return; 700185380Ssam } 701185380Ssam } 702185380Ssam } 703185380Ssam /* then lower spur immunity level, down to zero */ 704185380Ssam if (aniState->spurImmunityLevel > 0) { 705185380Ssam ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 706185380Ssam aniState->spurImmunityLevel - 1); 707185380Ssam return; 708185380Ssam } 709185380Ssam /* 710185380Ssam * if all else fails, lower noise immunity level down to a min value 711185380Ssam * zero for now 712185380Ssam */ 713185380Ssam if (aniState->noiseImmunityLevel > 0) { 714185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 715185380Ssam aniState->noiseImmunityLevel - 1); 716185380Ssam return; 717185380Ssam } 718185380Ssam} 719185380Ssam 720185380Ssam#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ 721185380Ssam/* convert HW counter values to ms using 11g clock rate, goo9d enough 722185380Ssam for 11a and Turbo */ 723185380Ssam 724185380Ssam/* 725185380Ssam * Return an approximation of the time spent ``listening'' by 726185380Ssam * deducting the cycles spent tx'ing and rx'ing from the total 727185380Ssam * cycle count since our last call. A return value <0 indicates 728185380Ssam * an invalid/inconsistent time. 729185380Ssam */ 730185380Ssamstatic int32_t 731185380Ssamar5416AniGetListenTime(struct ath_hal *ah) 732185380Ssam{ 733185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 734185380Ssam struct ar5212AniState *aniState; 735185380Ssam uint32_t txFrameCount, rxFrameCount, cycleCount; 736185380Ssam int32_t listenTime; 737185380Ssam 738185380Ssam txFrameCount = OS_REG_READ(ah, AR_TFCNT); 739185380Ssam rxFrameCount = OS_REG_READ(ah, AR_RFCNT); 740185380Ssam cycleCount = OS_REG_READ(ah, AR_CCCNT); 741185380Ssam 742185380Ssam aniState = ahp->ah_curani; 743185380Ssam if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { 744185380Ssam /* 745185380Ssam * Cycle counter wrap (or initial call); it's not possible 746185380Ssam * to accurately calculate a value because the registers 747185380Ssam * right shift rather than wrap--so punt and return 0. 748185380Ssam */ 749185380Ssam listenTime = 0; 750185380Ssam ahp->ah_stats.ast_ani_lzero++; 751185380Ssam } else { 752185380Ssam int32_t ccdelta = cycleCount - aniState->cycleCount; 753185380Ssam int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; 754185380Ssam int32_t tfdelta = txFrameCount - aniState->txFrameCount; 755185380Ssam listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; 756185380Ssam } 757185380Ssam aniState->cycleCount = cycleCount; 758185380Ssam aniState->txFrameCount = txFrameCount; 759185380Ssam aniState->rxFrameCount = rxFrameCount; 760185380Ssam return listenTime; 761185380Ssam} 762185380Ssam 763185380Ssam/* 764185380Ssam * Update ani stats in preparation for listen time processing. 765185380Ssam */ 766185380Ssamstatic void 767185380SsamupdateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) 768185380Ssam{ 769185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 770185380Ssam const struct ar5212AniParams *params = aniState->params; 771185380Ssam uint32_t phyCnt1, phyCnt2; 772185380Ssam int32_t ofdmPhyErrCnt, cckPhyErrCnt; 773185380Ssam 774185380Ssam /* Clear the mib counters and save them in the stats */ 775185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 776185380Ssam 777185380Ssam /* NB: these are not reset-on-read */ 778185380Ssam phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 779185380Ssam phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 780185380Ssam 781185380Ssam /* NB: these are spec'd to never roll-over */ 782185380Ssam ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 783185380Ssam if (ofdmPhyErrCnt < 0) { 784185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", 785185380Ssam ofdmPhyErrCnt, phyCnt1); 786185380Ssam ofdmPhyErrCnt = AR_PHY_COUNTMAX; 787185380Ssam } 788185380Ssam ahp->ah_stats.ast_ani_ofdmerrs += 789185380Ssam ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 790185380Ssam aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 791185380Ssam 792185380Ssam cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 793185380Ssam if (cckPhyErrCnt < 0) { 794185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", 795185380Ssam cckPhyErrCnt, phyCnt2); 796185380Ssam cckPhyErrCnt = AR_PHY_COUNTMAX; 797185380Ssam } 798185380Ssam ahp->ah_stats.ast_ani_cckerrs += 799185380Ssam cckPhyErrCnt - aniState->cckPhyErrCount; 800185380Ssam aniState->cckPhyErrCount = cckPhyErrCnt; 801185380Ssam} 802185380Ssam 803217684Sadrianvoid 804217684Sadrianar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, 805217684Sadrian const struct ieee80211_channel *chan) 806217684Sadrian{ 807217684Sadrian struct ath_hal_5212 *ahp = AH5212(ah); 808217684Sadrian ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; 809217684Sadrian} 810217684Sadrian 811185380Ssam/* 812185380Ssam * Do periodic processing. This routine is called from the 813185380Ssam * driver's rx interrupt handler after processing frames. 814185380Ssam */ 815185380Ssamvoid 816217684Sadrianar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) 817185380Ssam{ 818185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 819185380Ssam struct ar5212AniState *aniState = ahp->ah_curani; 820185380Ssam const struct ar5212AniParams *params; 821185380Ssam int32_t listenTime; 822185380Ssam 823185380Ssam /* XXX can aniState be null? */ 824185380Ssam if (aniState == AH_NULL) 825185380Ssam return; 826185380Ssam if (!ANI_ENA(ah)) 827185380Ssam return; 828185380Ssam 829185380Ssam listenTime = ar5416AniGetListenTime(ah); 830185380Ssam if (listenTime < 0) { 831185380Ssam ahp->ah_stats.ast_ani_lneg++; 832185380Ssam /* restart ANI period if listenTime is invalid */ 833185380Ssam ar5416AniRestart(ah, aniState); 834185380Ssam } 835185380Ssam /* XXX beware of overflow? */ 836185380Ssam aniState->listenTime += listenTime; 837185380Ssam 838185380Ssam OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); 839185380Ssam 840185380Ssam params = aniState->params; 841185380Ssam if (aniState->listenTime > 5*params->period) { 842185380Ssam /* 843185380Ssam * Check to see if need to lower immunity if 844185380Ssam * 5 aniPeriods have passed 845185380Ssam */ 846185380Ssam updateMIBStats(ah, aniState); 847185380Ssam if (aniState->ofdmPhyErrCount <= aniState->listenTime * 848185380Ssam params->ofdmTrigLow/1000 && 849185380Ssam aniState->cckPhyErrCount <= aniState->listenTime * 850185380Ssam params->cckTrigLow/1000) 851185380Ssam ar5416AniLowerImmunity(ah); 852185380Ssam ar5416AniRestart(ah, aniState); 853185380Ssam } else if (aniState->listenTime > params->period) { 854185380Ssam updateMIBStats(ah, aniState); 855185380Ssam /* check to see if need to raise immunity */ 856185380Ssam if (aniState->ofdmPhyErrCount > aniState->listenTime * 857185380Ssam params->ofdmTrigHigh / 1000) { 858219767Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 859219767Sadrian "%s: OFDM err %u listenTime %u\n", __func__, 860219767Sadrian aniState->ofdmPhyErrCount, aniState->listenTime); 861185380Ssam ar5416AniOfdmErrTrigger(ah); 862185380Ssam ar5416AniRestart(ah, aniState); 863185380Ssam } else if (aniState->cckPhyErrCount > aniState->listenTime * 864185380Ssam params->cckTrigHigh / 1000) { 865219767Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 866219767Sadrian "%s: CCK err %u listenTime %u\n", __func__, 867219767Sadrian aniState->ofdmPhyErrCount, aniState->listenTime); 868185380Ssam ar5416AniCckErrTrigger(ah); 869185380Ssam ar5416AniRestart(ah, aniState); 870185380Ssam } 871185380Ssam } 872185380Ssam} 873