1 | /* |
2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
3 | * Copyright (c) 2002-2008 Atheros Communications, Inc. |
4 | * |
5 | * Permission to use, copy, modify, and/or distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | * |
17 | * $Id: ar5212_ani.c,v 1.2 2011/03/07 11:25:43 cegger Exp $ |
18 | */ |
19 | #include "opt_ah.h" |
20 | |
21 | #include "ah.h" |
22 | #include "ah_internal.h" |
23 | #include "ah_desc.h" |
24 | |
25 | #include "ar5212/ar5212.h" |
26 | #include "ar5212/ar5212reg.h" |
27 | #include "ar5212/ar5212phy.h" |
28 | |
29 | /* |
30 | * Anti noise immunity support. We track phy errors and react |
31 | * to excessive errors by adjusting the noise immunity parameters. |
32 | */ |
33 | |
34 | #define HAL_EP_RND(x, mul) \ |
35 | ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) |
36 | #define (ahp) \ |
37 | HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ |
38 | HAL_RSSI_EP_MULTIPLIER) |
39 | |
40 | /* |
41 | * ANI processing tunes radio parameters according to PHY errors |
42 | * and related information. This is done for for noise and spur |
43 | * immunity in all operating modes if the device indicates it's |
44 | * capable at attach time. In addition, when there is a reference |
45 | * rssi value (e.g. beacon frames from an ap in station mode) |
46 | * further tuning is done. |
47 | * |
48 | * ANI_ENA indicates whether any ANI processing should be done; |
49 | * this is specified at attach time. |
50 | * |
51 | * ANI_ENA_RSSI indicates whether rssi-based processing should |
52 | * done, this is enabled based on operating mode and is meaningful |
53 | * only if ANI_ENA is true. |
54 | * |
55 | * ANI parameters are typically controlled only by the hal. The |
56 | * AniControl interface however permits manual tuning through the |
57 | * diagnostic api. |
58 | */ |
59 | #define ANI_ENA(ah) \ |
60 | (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) |
61 | #define (ah) \ |
62 | (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) |
63 | |
64 | #define ah_mibStats ah_stats.ast_mibstats |
65 | |
66 | static void |
67 | enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) |
68 | { |
69 | struct ath_hal_5212 *ahp = AH5212(ah); |
70 | |
71 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " |
72 | "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n" , |
73 | __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); |
74 | |
75 | OS_REG_WRITE(ah, AR_FILTOFDM, 0); |
76 | OS_REG_WRITE(ah, AR_FILTCCK, 0); |
77 | |
78 | OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); |
79 | OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); |
80 | OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING); |
81 | OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING); |
82 | |
83 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ |
84 | ar5212EnableMibCounters(ah); /* enable everything */ |
85 | } |
86 | |
87 | static void |
88 | disableAniMIBCounters(struct ath_hal *ah) |
89 | { |
90 | struct ath_hal_5212 *ahp = AH5212(ah); |
91 | |
92 | HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n" ); |
93 | |
94 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ |
95 | ar5212DisableMibCounters(ah); /* disable everything */ |
96 | |
97 | OS_REG_WRITE(ah, AR_PHYCNTMASK1, 0); |
98 | OS_REG_WRITE(ah, AR_PHYCNTMASK2, 0); |
99 | } |
100 | |
101 | /* |
102 | * This routine returns the index into the aniState array that |
103 | * corresponds to the channel in *chan. If no match is found and the |
104 | * array is still not fully utilized, a new entry is created for the |
105 | * channel. We assume the attach function has already initialized the |
106 | * ah_ani values and only the channel field needs to be set. |
107 | */ |
108 | static int |
109 | ar5212GetAniChannelIndex(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan) |
110 | { |
111 | #define N(a) (sizeof(a) / sizeof(a[0])) |
112 | struct ath_hal_5212 *ahp = AH5212(ah); |
113 | int i; |
114 | |
115 | for (i = 0; i < N(ahp->ah_ani); i++) { |
116 | struct ar5212AniState *asp = &ahp->ah_ani[i]; |
117 | if (asp->c.channel == chan->channel) |
118 | return i; |
119 | if (asp->c.channel == 0) { |
120 | asp->c.channel = chan->channel; |
121 | asp->c.channelFlags = chan->channelFlags; |
122 | asp->c.privFlags = chan->privFlags; |
123 | asp->isSetup = AH_FALSE; |
124 | if (IS_CHAN_2GHZ(chan)) |
125 | asp->params = &ahp->ah_aniParams24; |
126 | else |
127 | asp->params = &ahp->ah_aniParams5; |
128 | return i; |
129 | } |
130 | } |
131 | /* XXX statistic */ |
132 | HALDEBUG(ah, HAL_DEBUG_ANY, |
133 | "No more channel states left. Using channel 0\n" ); |
134 | return 0; /* XXX gotta return something valid */ |
135 | #undef N |
136 | } |
137 | |
138 | /* |
139 | * Return the current ANI state of the channel we're on |
140 | */ |
141 | struct ar5212AniState * |
142 | ar5212AniGetCurrentState(struct ath_hal *ah) |
143 | { |
144 | return AH5212(ah)->ah_curani; |
145 | } |
146 | |
147 | /* |
148 | * Return the current statistics. |
149 | */ |
150 | struct ar5212Stats * |
151 | ar5212AniGetCurrentStats(struct ath_hal *ah) |
152 | { |
153 | struct ath_hal_5212 *ahp = AH5212(ah); |
154 | |
155 | /* update mib stats so we return current data */ |
156 | /* XXX? side-effects to doing this here? */ |
157 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); |
158 | return &ahp->ah_stats; |
159 | } |
160 | |
161 | static void |
162 | setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) |
163 | { |
164 | if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { |
165 | HALDEBUG(ah, HAL_DEBUG_ANY, |
166 | "OFDM Trigger %d is too high for hw counters, using max\n" , |
167 | params->ofdmTrigHigh); |
168 | params->ofdmPhyErrBase = 0; |
169 | } else |
170 | params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; |
171 | if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { |
172 | HALDEBUG(ah, HAL_DEBUG_ANY, |
173 | "CCK Trigger %d is too high for hw counters, using max\n" , |
174 | params->cckTrigHigh); |
175 | params->cckPhyErrBase = 0; |
176 | } else |
177 | params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; |
178 | } |
179 | |
180 | /* |
181 | * Setup ANI handling. Sets all thresholds and reset the |
182 | * channel statistics. Note that ar5212AniReset should be |
183 | * called by ar5212Reset before anything else happens and |
184 | * that's where we force initial settings. |
185 | */ |
186 | void |
187 | ar5212AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, |
188 | const struct ar5212AniParams *params5, HAL_BOOL enable) |
189 | { |
190 | struct ath_hal_5212 *ahp = AH5212(ah); |
191 | |
192 | ahp->ah_hasHwPhyCounters = |
193 | AH_PRIVATE(ah)->ah_caps.halHwPhyCounterSupport; |
194 | |
195 | if (params24 != AH_NULL) { |
196 | OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); |
197 | setPhyErrBase(ah, &ahp->ah_aniParams24); |
198 | } |
199 | if (params5 != AH_NULL) { |
200 | OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); |
201 | setPhyErrBase(ah, &ahp->ah_aniParams5); |
202 | } |
203 | |
204 | OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); |
205 | if (ahp->ah_hasHwPhyCounters) { |
206 | /* Enable MIB Counters */ |
207 | enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); |
208 | } |
209 | if (enable) { /* Enable ani now */ |
210 | HALASSERT(params24 != AH_NULL && params5 != AH_NULL); |
211 | ahp->ah_procPhyErr |= HAL_ANI_ENA; |
212 | } else { |
213 | ahp->ah_procPhyErr &= ~HAL_ANI_ENA; |
214 | } |
215 | } |
216 | |
217 | HAL_BOOL |
218 | ar5212AniSetParams(struct ath_hal *ah, const struct ar5212AniParams *params24, |
219 | const struct ar5212AniParams *params5) |
220 | { |
221 | struct ath_hal_5212 *ahp = AH5212(ah); |
222 | HAL_BOOL ena = (ahp->ah_procPhyErr & HAL_ANI_ENA) != 0; |
223 | |
224 | ar5212AniControl(ah, HAL_ANI_MODE, AH_FALSE); |
225 | |
226 | OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); |
227 | setPhyErrBase(ah, &ahp->ah_aniParams24); |
228 | OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); |
229 | setPhyErrBase(ah, &ahp->ah_aniParams5); |
230 | |
231 | OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); |
232 | ar5212AniReset(ah, AH_PRIVATE(ah)->ah_curchan, |
233 | AH_PRIVATE(ah)->ah_opmode, AH_FALSE); |
234 | |
235 | ar5212AniControl(ah, HAL_ANI_MODE, ena); |
236 | |
237 | return AH_TRUE; |
238 | } |
239 | |
240 | /* |
241 | * Cleanup any ANI state setup. |
242 | */ |
243 | void |
244 | ar5212AniDetach(struct ath_hal *ah) |
245 | { |
246 | struct ath_hal_5212 *ahp = AH5212(ah); |
247 | |
248 | HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n" ); |
249 | if (ahp->ah_hasHwPhyCounters) |
250 | disableAniMIBCounters(ah); |
251 | } |
252 | |
253 | /* |
254 | * Control Adaptive Noise Immunity Parameters |
255 | */ |
256 | HAL_BOOL |
257 | ar5212AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) |
258 | { |
259 | typedef int TABLE[]; |
260 | struct ath_hal_5212 *ahp = AH5212(ah); |
261 | struct ar5212AniState *aniState = ahp->ah_curani; |
262 | const struct ar5212AniParams *params = aniState->params; |
263 | |
264 | OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); |
265 | |
266 | switch (cmd) { |
267 | case HAL_ANI_NOISE_IMMUNITY_LEVEL: { |
268 | u_int level = param; |
269 | |
270 | if (level > params->maxNoiseImmunityLevel) { |
271 | HALDEBUG(ah, HAL_DEBUG_ANY, |
272 | "%s: level out of range (%u > %u)\n" , |
273 | __func__, level, params->maxNoiseImmunityLevel); |
274 | return AH_FALSE; |
275 | } |
276 | |
277 | OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, |
278 | AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); |
279 | OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, |
280 | AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); |
281 | OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, |
282 | AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); |
283 | OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, |
284 | AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); |
285 | |
286 | if (level > aniState->noiseImmunityLevel) |
287 | ahp->ah_stats.ast_ani_niup++; |
288 | else if (level < aniState->noiseImmunityLevel) |
289 | ahp->ah_stats.ast_ani_nidown++; |
290 | aniState->noiseImmunityLevel = level; |
291 | break; |
292 | } |
293 | case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { |
294 | static const TABLE m1ThreshLow = { 127, 50 }; |
295 | static const TABLE m2ThreshLow = { 127, 40 }; |
296 | static const TABLE m1Thresh = { 127, 0x4d }; |
297 | static const TABLE m2Thresh = { 127, 0x40 }; |
298 | static const TABLE m2CountThr = { 31, 16 }; |
299 | static const TABLE m2CountThrLow = { 63, 48 }; |
300 | u_int on = param ? 1 : 0; |
301 | |
302 | OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, |
303 | AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); |
304 | OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, |
305 | AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); |
306 | OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, |
307 | AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); |
308 | OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, |
309 | AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); |
310 | OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, |
311 | AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); |
312 | OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, |
313 | AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); |
314 | |
315 | if (on) { |
316 | OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, |
317 | AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); |
318 | ahp->ah_stats.ast_ani_ofdmon++; |
319 | } else { |
320 | OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, |
321 | AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); |
322 | ahp->ah_stats.ast_ani_ofdmoff++; |
323 | } |
324 | aniState->ofdmWeakSigDetectOff = !on; |
325 | break; |
326 | } |
327 | case HAL_ANI_CCK_WEAK_SIGNAL_THR: { |
328 | static const TABLE weakSigThrCck = { 8, 6 }; |
329 | u_int high = param ? 1 : 0; |
330 | |
331 | OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, |
332 | AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); |
333 | if (high) |
334 | ahp->ah_stats.ast_ani_cckhigh++; |
335 | else |
336 | ahp->ah_stats.ast_ani_ccklow++; |
337 | aniState->cckWeakSigThreshold = high; |
338 | break; |
339 | } |
340 | case HAL_ANI_FIRSTEP_LEVEL: { |
341 | u_int level = param; |
342 | |
343 | if (level > params->maxFirstepLevel) { |
344 | HALDEBUG(ah, HAL_DEBUG_ANY, |
345 | "%s: level out of range (%u > %u)\n" , |
346 | __func__, level, params->maxFirstepLevel); |
347 | return AH_FALSE; |
348 | } |
349 | OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, |
350 | AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); |
351 | if (level > aniState->firstepLevel) |
352 | ahp->ah_stats.ast_ani_stepup++; |
353 | else if (level < aniState->firstepLevel) |
354 | ahp->ah_stats.ast_ani_stepdown++; |
355 | aniState->firstepLevel = level; |
356 | break; |
357 | } |
358 | case HAL_ANI_SPUR_IMMUNITY_LEVEL: { |
359 | u_int level = param; |
360 | |
361 | if (level > params->maxSpurImmunityLevel) { |
362 | HALDEBUG(ah, HAL_DEBUG_ANY, |
363 | "%s: level out of range (%u > %u)\n" , |
364 | __func__, level, params->maxSpurImmunityLevel); |
365 | return AH_FALSE; |
366 | } |
367 | OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, |
368 | AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); |
369 | if (level > aniState->spurImmunityLevel) |
370 | ahp->ah_stats.ast_ani_spurup++; |
371 | else if (level < aniState->spurImmunityLevel) |
372 | ahp->ah_stats.ast_ani_spurdown++; |
373 | aniState->spurImmunityLevel = level; |
374 | break; |
375 | } |
376 | case HAL_ANI_PRESENT: |
377 | break; |
378 | case HAL_ANI_MODE: |
379 | if (param == 0) { |
380 | ahp->ah_procPhyErr &= ~HAL_ANI_ENA; |
381 | /* Turn off HW counters if we have them */ |
382 | ar5212AniDetach(ah); |
383 | ar5212SetRxFilter(ah, |
384 | ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); |
385 | } else { /* normal/auto mode */ |
386 | /* don't mess with state if already enabled */ |
387 | if (ahp->ah_procPhyErr & HAL_ANI_ENA) |
388 | break; |
389 | if (ahp->ah_hasHwPhyCounters) { |
390 | ar5212SetRxFilter(ah, |
391 | ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); |
392 | /* Enable MIB Counters */ |
393 | enableAniMIBCounters(ah, |
394 | ahp->ah_curani != AH_NULL ? |
395 | ahp->ah_curani->params: |
396 | &ahp->ah_aniParams24 /*XXX*/); |
397 | } else { |
398 | ar5212SetRxFilter(ah, |
399 | ar5212GetRxFilter(ah) | HAL_RX_FILTER_PHYERR); |
400 | } |
401 | ahp->ah_procPhyErr |= HAL_ANI_ENA; |
402 | } |
403 | break; |
404 | #ifdef AH_PRIVATE_DIAG |
405 | case HAL_ANI_PHYERR_RESET: |
406 | ahp->ah_stats.ast_ani_ofdmerrs = 0; |
407 | ahp->ah_stats.ast_ani_cckerrs = 0; |
408 | break; |
409 | #endif /* AH_PRIVATE_DIAG */ |
410 | default: |
411 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid cmd %u\n" , |
412 | __func__, cmd); |
413 | return AH_FALSE; |
414 | } |
415 | return AH_TRUE; |
416 | } |
417 | |
418 | static void |
419 | ar5212AniOfdmErrTrigger(struct ath_hal *ah) |
420 | { |
421 | struct ath_hal_5212 *ahp = AH5212(ah); |
422 | HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan; |
423 | struct ar5212AniState *aniState; |
424 | const struct ar5212AniParams *params; |
425 | |
426 | HALASSERT(chan != AH_NULL); |
427 | |
428 | if (!ANI_ENA(ah)) |
429 | return; |
430 | |
431 | aniState = ahp->ah_curani; |
432 | params = aniState->params; |
433 | /* First, raise noise immunity level, up to max */ |
434 | if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { |
435 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n" , __func__, |
436 | aniState->noiseImmunityLevel + 1); |
437 | ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, |
438 | aniState->noiseImmunityLevel + 1); |
439 | return; |
440 | } |
441 | /* then, raise spur immunity level, up to max */ |
442 | if (aniState->spurImmunityLevel+1 <= params->maxSpurImmunityLevel) { |
443 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise SI to %u\n" , __func__, |
444 | aniState->spurImmunityLevel + 1); |
445 | ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, |
446 | aniState->spurImmunityLevel + 1); |
447 | return; |
448 | } |
449 | |
450 | if (ANI_ENA_RSSI(ah)) { |
451 | int32_t = BEACON_RSSI(ahp); |
452 | if (rssi > params->rssiThrHigh) { |
453 | /* |
454 | * Beacon rssi is high, can turn off ofdm |
455 | * weak sig detect. |
456 | */ |
457 | if (!aniState->ofdmWeakSigDetectOff) { |
458 | HALDEBUG(ah, HAL_DEBUG_ANI, |
459 | "%s: rssi %d OWSD off\n" , __func__, rssi); |
460 | ar5212AniControl(ah, |
461 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
462 | AH_FALSE); |
463 | ar5212AniControl(ah, |
464 | HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); |
465 | return; |
466 | } |
467 | /* |
468 | * If weak sig detect is already off, as last resort, |
469 | * raise firstep level |
470 | */ |
471 | if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { |
472 | HALDEBUG(ah, HAL_DEBUG_ANI, |
473 | "%s: rssi %d raise ST %u\n" , __func__, rssi, |
474 | aniState->firstepLevel+1); |
475 | ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
476 | aniState->firstepLevel + 1); |
477 | return; |
478 | } |
479 | } else if (rssi > params->rssiThrLow) { |
480 | /* |
481 | * Beacon rssi in mid range, need ofdm weak signal |
482 | * detect, but we can raise firststepLevel. |
483 | */ |
484 | if (aniState->ofdmWeakSigDetectOff) { |
485 | HALDEBUG(ah, HAL_DEBUG_ANI, |
486 | "%s: rssi %d OWSD on\n" , __func__, rssi); |
487 | ar5212AniControl(ah, |
488 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
489 | AH_TRUE); |
490 | } |
491 | if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { |
492 | HALDEBUG(ah, HAL_DEBUG_ANI, |
493 | "%s: rssi %d raise ST %u\n" , __func__, rssi, |
494 | aniState->firstepLevel+1); |
495 | ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
496 | aniState->firstepLevel + 1); |
497 | } |
498 | return; |
499 | } else { |
500 | /* |
501 | * Beacon rssi is low, if in 11b/g mode, turn off ofdm |
502 | * weak signal detection and zero firstepLevel to |
503 | * maximize CCK sensitivity |
504 | */ |
505 | /* XXX can optimize */ |
506 | if (IS_CHAN_B(chan) || IS_CHAN_G(chan)) { |
507 | if (!aniState->ofdmWeakSigDetectOff) { |
508 | HALDEBUG(ah, HAL_DEBUG_ANI, |
509 | "%s: rssi %d OWSD off\n" , |
510 | __func__, rssi); |
511 | ar5212AniControl(ah, |
512 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
513 | AH_FALSE); |
514 | } |
515 | if (aniState->firstepLevel > 0) { |
516 | HALDEBUG(ah, HAL_DEBUG_ANI, |
517 | "%s: rssi %d zero ST (was %u)\n" , |
518 | __func__, rssi, |
519 | aniState->firstepLevel); |
520 | ar5212AniControl(ah, |
521 | HAL_ANI_FIRSTEP_LEVEL, 0); |
522 | } |
523 | return; |
524 | } |
525 | } |
526 | } |
527 | } |
528 | |
529 | static void |
530 | ar5212AniCckErrTrigger(struct ath_hal *ah) |
531 | { |
532 | struct ath_hal_5212 *ahp = AH5212(ah); |
533 | HAL_CHANNEL_INTERNAL *chan = AH_PRIVATE(ah)->ah_curchan; |
534 | struct ar5212AniState *aniState; |
535 | const struct ar5212AniParams *params; |
536 | |
537 | HALASSERT(chan != AH_NULL); |
538 | |
539 | if (!ANI_ENA(ah)) |
540 | return; |
541 | |
542 | /* first, raise noise immunity level, up to max */ |
543 | aniState = ahp->ah_curani; |
544 | params = aniState->params; |
545 | if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { |
546 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n" , __func__, |
547 | aniState->noiseImmunityLevel + 1); |
548 | ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, |
549 | aniState->noiseImmunityLevel + 1); |
550 | return; |
551 | } |
552 | |
553 | if (ANI_ENA_RSSI(ah)) { |
554 | int32_t = BEACON_RSSI(ahp); |
555 | if (rssi > params->rssiThrLow) { |
556 | /* |
557 | * Beacon signal in mid and high range, |
558 | * raise firstep level. |
559 | */ |
560 | if (aniState->firstepLevel+1 < params->maxFirstepLevel) { |
561 | HALDEBUG(ah, HAL_DEBUG_ANI, |
562 | "%s: rssi %d raise ST %u\n" , __func__, rssi, |
563 | aniState->firstepLevel+1); |
564 | ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
565 | aniState->firstepLevel + 1); |
566 | } |
567 | } else { |
568 | /* |
569 | * Beacon rssi is low, zero firstep level to maximize |
570 | * CCK sensitivity in 11b/g mode. |
571 | */ |
572 | /* XXX can optimize */ |
573 | if (IS_CHAN_B(chan) || IS_CHAN_G(chan)) { |
574 | if (aniState->firstepLevel > 0) { |
575 | HALDEBUG(ah, HAL_DEBUG_ANI, |
576 | "%s: rssi %d zero ST (was %u)\n" , |
577 | __func__, rssi, |
578 | aniState->firstepLevel); |
579 | ar5212AniControl(ah, |
580 | HAL_ANI_FIRSTEP_LEVEL, 0); |
581 | } |
582 | } |
583 | } |
584 | } |
585 | } |
586 | |
587 | static void |
588 | ar5212AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) |
589 | { |
590 | struct ath_hal_5212 *ahp = AH5212(ah); |
591 | |
592 | aniState->listenTime = 0; |
593 | if (ahp->ah_hasHwPhyCounters) { |
594 | const struct ar5212AniParams *params = aniState->params; |
595 | /* |
596 | * NB: these are written on reset based on the |
597 | * ini so we must re-write them! |
598 | */ |
599 | OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); |
600 | OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); |
601 | OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING); |
602 | OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING); |
603 | |
604 | /* Clear the mib counters and save them in the stats */ |
605 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); |
606 | } |
607 | aniState->ofdmPhyErrCount = 0; |
608 | aniState->cckPhyErrCount = 0; |
609 | } |
610 | |
611 | /* |
612 | * Restore/reset the ANI parameters and reset the statistics. |
613 | * This routine must be called for every channel change. |
614 | * |
615 | * NOTE: This is where ah_curani is set; other ani code assumes |
616 | * it is setup to reflect the current channel. |
617 | */ |
618 | void |
619 | ar5212AniReset(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, |
620 | HAL_OPMODE opmode, int restore) |
621 | { |
622 | struct ath_hal_5212 *ahp = AH5212(ah); |
623 | struct ar5212AniState *aniState; |
624 | uint32_t rxfilter; |
625 | int index; |
626 | |
627 | index = ar5212GetAniChannelIndex(ah, chan); |
628 | aniState = &ahp->ah_ani[index]; |
629 | ahp->ah_curani = aniState; |
630 | #if 0 |
631 | ath_hal_printf(ah,"%s: chan %u/0x%x restore %d setup %d opmode %u\n" , |
632 | __func__, chan->channel, chan->channelFlags, restore, |
633 | aniState->isSetup, opmode); |
634 | #else |
635 | HALDEBUG(ah, HAL_DEBUG_ANI, |
636 | "%s: chan %u/0x%x restore %d setup %d opmode %u\n" , |
637 | __func__, chan->channel, chan->channelFlags, restore, |
638 | aniState->isSetup, opmode); |
639 | #endif |
640 | OS_MARK(ah, AH_MARK_ANI_RESET, opmode); |
641 | |
642 | /* |
643 | * Turn off PHY error frame delivery while we futz with settings. |
644 | */ |
645 | rxfilter = ar5212GetRxFilter(ah); |
646 | ar5212SetRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); |
647 | /* |
648 | * Automatic processing is done only in station mode right now. |
649 | */ |
650 | if (opmode == HAL_M_STA) |
651 | ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; |
652 | else |
653 | ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; |
654 | /* |
655 | * Set all ani parameters. We either set them to initial |
656 | * values or restore the previous ones for the channel. |
657 | * XXX if ANI follows hardware, we don't care what mode we're |
658 | * XXX in, we should keep the ani parameters |
659 | */ |
660 | if (restore && aniState->isSetup) { |
661 | ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, |
662 | aniState->noiseImmunityLevel); |
663 | ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, |
664 | aniState->spurImmunityLevel); |
665 | ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
666 | !aniState->ofdmWeakSigDetectOff); |
667 | ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, |
668 | aniState->cckWeakSigThreshold); |
669 | ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
670 | aniState->firstepLevel); |
671 | } else { |
672 | ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); |
673 | ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); |
674 | ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
675 | AH_TRUE); |
676 | ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); |
677 | ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); |
678 | aniState->isSetup = AH_TRUE; |
679 | } |
680 | ar5212AniRestart(ah, aniState); |
681 | |
682 | /* restore RX filter mask */ |
683 | ar5212SetRxFilter(ah, rxfilter); |
684 | } |
685 | |
686 | /* |
687 | * Process a MIB interrupt. We may potentially be invoked because |
688 | * any of the MIB counters overflow/trigger so don't assume we're |
689 | * here because a PHY error counter triggered. |
690 | */ |
691 | void |
692 | ar5212ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) |
693 | { |
694 | struct ath_hal_5212 *ahp = AH5212(ah); |
695 | uint32_t phyCnt1, phyCnt2; |
696 | |
697 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " |
698 | "filtofdm 0x%x filtcck 0x%x\n" , |
699 | __func__, OS_REG_READ(ah, AR_MIBC), |
700 | OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), |
701 | OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); |
702 | |
703 | /* |
704 | * First order of business is to clear whatever caused |
705 | * the interrupt so we don't keep getting interrupted. |
706 | * We have the usual mib counters that are reset-on-read |
707 | * and the additional counters that appeared starting in |
708 | * Hainan. We collect the mib counters and explicitly |
709 | * zero additional counters we are not using. Anything |
710 | * else is reset only if it caused the interrupt. |
711 | */ |
712 | /* NB: these are not reset-on-read */ |
713 | phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1); |
714 | phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2); |
715 | /* not used, always reset them in case they are the cause */ |
716 | OS_REG_WRITE(ah, AR_FILTOFDM, 0); |
717 | OS_REG_WRITE(ah, AR_FILTCCK, 0); |
718 | |
719 | /* Clear the mib counters and save them in the stats */ |
720 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); |
721 | ahp->ah_stats.ast_nodestats = *stats; |
722 | |
723 | /* |
724 | * Check for an ani stat hitting the trigger threshold. |
725 | * When this happens we get a MIB interrupt and the top |
726 | * 2 bits of the counter register will be 0b11, hence |
727 | * the mask check of phyCnt?. |
728 | */ |
729 | if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || |
730 | ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { |
731 | struct ar5212AniState *aniState = ahp->ah_curani; |
732 | const struct ar5212AniParams *params = aniState->params; |
733 | uint32_t ofdmPhyErrCnt, cckPhyErrCnt; |
734 | |
735 | ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; |
736 | ahp->ah_stats.ast_ani_ofdmerrs += |
737 | ofdmPhyErrCnt - aniState->ofdmPhyErrCount; |
738 | aniState->ofdmPhyErrCount = ofdmPhyErrCnt; |
739 | |
740 | cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; |
741 | ahp->ah_stats.ast_ani_cckerrs += |
742 | cckPhyErrCnt - aniState->cckPhyErrCount; |
743 | aniState->cckPhyErrCount = cckPhyErrCnt; |
744 | |
745 | /* |
746 | * NB: figure out which counter triggered. If both |
747 | * trigger we'll only deal with one as the processing |
748 | * clobbers the error counter so the trigger threshold |
749 | * check will never be true. |
750 | */ |
751 | if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) |
752 | ar5212AniOfdmErrTrigger(ah); |
753 | if (aniState->cckPhyErrCount > params->cckTrigHigh) |
754 | ar5212AniCckErrTrigger(ah); |
755 | /* NB: always restart to insure the h/w counters are reset */ |
756 | ar5212AniRestart(ah, aniState); |
757 | } |
758 | } |
759 | |
760 | void |
761 | ar5212AniPhyErrReport(struct ath_hal *ah, const struct ath_rx_status *rs) |
762 | { |
763 | struct ath_hal_5212 *ahp = AH5212(ah); |
764 | struct ar5212AniState *aniState; |
765 | const struct ar5212AniParams *params; |
766 | |
767 | HALASSERT(!ahp->ah_hasHwPhyCounters && rs != AH_NULL); |
768 | |
769 | aniState = ahp->ah_curani; |
770 | params = aniState->params; |
771 | if (rs->rs_phyerr == HAL_PHYERR_OFDM_TIMING) { |
772 | aniState->ofdmPhyErrCount++; |
773 | ahp->ah_stats.ast_ani_ofdmerrs++; |
774 | if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) { |
775 | ar5212AniOfdmErrTrigger(ah); |
776 | ar5212AniRestart(ah, aniState); |
777 | } |
778 | } else if (rs->rs_phyerr == HAL_PHYERR_CCK_TIMING) { |
779 | aniState->cckPhyErrCount++; |
780 | ahp->ah_stats.ast_ani_cckerrs++; |
781 | if (aniState->cckPhyErrCount > params->cckTrigHigh) { |
782 | ar5212AniCckErrTrigger(ah); |
783 | ar5212AniRestart(ah, aniState); |
784 | } |
785 | } |
786 | } |
787 | |
788 | static void |
789 | ar5212AniLowerImmunity(struct ath_hal *ah) |
790 | { |
791 | struct ath_hal_5212 *ahp = AH5212(ah); |
792 | struct ar5212AniState *aniState; |
793 | const struct ar5212AniParams *params; |
794 | |
795 | HALASSERT(ANI_ENA(ah)); |
796 | |
797 | aniState = ahp->ah_curani; |
798 | params = aniState->params; |
799 | if (ANI_ENA_RSSI(ah)) { |
800 | int32_t = BEACON_RSSI(ahp); |
801 | if (rssi > params->rssiThrHigh) { |
802 | /* |
803 | * Beacon signal is high, leave ofdm weak signal |
804 | * detection off or it may oscillate. Let it fall |
805 | * through. |
806 | */ |
807 | } else if (rssi > params->rssiThrLow) { |
808 | /* |
809 | * Beacon rssi in mid range, turn on ofdm weak signal |
810 | * detection or lower firstep level. |
811 | */ |
812 | if (aniState->ofdmWeakSigDetectOff) { |
813 | HALDEBUG(ah, HAL_DEBUG_ANI, |
814 | "%s: rssi %d OWSD on\n" , __func__, rssi); |
815 | ar5212AniControl(ah, |
816 | HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, |
817 | AH_TRUE); |
818 | return; |
819 | } |
820 | if (aniState->firstepLevel > 0) { |
821 | HALDEBUG(ah, HAL_DEBUG_ANI, |
822 | "%s: rssi %d lower ST %u\n" , __func__, rssi, |
823 | aniState->firstepLevel-1); |
824 | ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
825 | aniState->firstepLevel - 1); |
826 | return; |
827 | } |
828 | } else { |
829 | /* |
830 | * Beacon rssi is low, reduce firstep level. |
831 | */ |
832 | if (aniState->firstepLevel > 0) { |
833 | HALDEBUG(ah, HAL_DEBUG_ANI, |
834 | "%s: rssi %d lower ST %u\n" , __func__, rssi, |
835 | aniState->firstepLevel-1); |
836 | ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, |
837 | aniState->firstepLevel - 1); |
838 | return; |
839 | } |
840 | } |
841 | } |
842 | /* then lower spur immunity level, down to zero */ |
843 | if (aniState->spurImmunityLevel > 0) { |
844 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower SI %u\n" , |
845 | __func__, aniState->spurImmunityLevel-1); |
846 | ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, |
847 | aniState->spurImmunityLevel - 1); |
848 | return; |
849 | } |
850 | /* |
851 | * if all else fails, lower noise immunity level down to a min value |
852 | * zero for now |
853 | */ |
854 | if (aniState->noiseImmunityLevel > 0) { |
855 | HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower NI %u\n" , |
856 | __func__, aniState->noiseImmunityLevel-1); |
857 | ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, |
858 | aniState->noiseImmunityLevel - 1); |
859 | return; |
860 | } |
861 | } |
862 | |
863 | #define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ |
864 | /* convert HW counter values to ms using 11g clock rate, goo9d enough |
865 | for 11a and Turbo */ |
866 | |
867 | /* |
868 | * Return an approximation of the time spent ``listening'' by |
869 | * deducting the cycles spent tx'ing and rx'ing from the total |
870 | * cycle count since our last call. A return value <0 indicates |
871 | * an invalid/inconsistent time. |
872 | */ |
873 | static int32_t |
874 | ar5212AniGetListenTime(struct ath_hal *ah) |
875 | { |
876 | struct ath_hal_5212 *ahp = AH5212(ah); |
877 | struct ar5212AniState *aniState; |
878 | uint32_t txFrameCount, rxFrameCount, cycleCount; |
879 | int32_t listenTime; |
880 | |
881 | txFrameCount = OS_REG_READ(ah, AR_TFCNT); |
882 | rxFrameCount = OS_REG_READ(ah, AR_RFCNT); |
883 | cycleCount = OS_REG_READ(ah, AR_CCCNT); |
884 | |
885 | aniState = ahp->ah_curani; |
886 | if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { |
887 | /* |
888 | * Cycle counter wrap (or initial call); it's not possible |
889 | * to accurately calculate a value because the registers |
890 | * right shift rather than wrap--so punt and return 0. |
891 | */ |
892 | listenTime = 0; |
893 | ahp->ah_stats.ast_ani_lzero++; |
894 | } else { |
895 | int32_t ccdelta = cycleCount - aniState->cycleCount; |
896 | int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; |
897 | int32_t tfdelta = txFrameCount - aniState->txFrameCount; |
898 | listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; |
899 | } |
900 | aniState->cycleCount = cycleCount; |
901 | aniState->txFrameCount = txFrameCount; |
902 | aniState->rxFrameCount = rxFrameCount; |
903 | return listenTime; |
904 | } |
905 | |
906 | /* |
907 | * Update ani stats in preparation for listen time processing. |
908 | */ |
909 | static void |
910 | updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) |
911 | { |
912 | struct ath_hal_5212 *ahp = AH5212(ah); |
913 | const struct ar5212AniParams *params = aniState->params; |
914 | uint32_t phyCnt1, phyCnt2; |
915 | int32_t ofdmPhyErrCnt, cckPhyErrCnt; |
916 | |
917 | HALASSERT(ahp->ah_hasHwPhyCounters); |
918 | |
919 | /* Clear the mib counters and save them in the stats */ |
920 | ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); |
921 | |
922 | /* NB: these are not reset-on-read */ |
923 | phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1); |
924 | phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2); |
925 | |
926 | /* NB: these are spec'd to never roll-over */ |
927 | ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; |
928 | if (ofdmPhyErrCnt < 0) { |
929 | HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n" , |
930 | ofdmPhyErrCnt, phyCnt1); |
931 | ofdmPhyErrCnt = AR_PHY_COUNTMAX; |
932 | } |
933 | ahp->ah_stats.ast_ani_ofdmerrs += |
934 | ofdmPhyErrCnt - aniState->ofdmPhyErrCount; |
935 | aniState->ofdmPhyErrCount = ofdmPhyErrCnt; |
936 | |
937 | cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; |
938 | if (cckPhyErrCnt < 0) { |
939 | HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n" , |
940 | cckPhyErrCnt, phyCnt2); |
941 | cckPhyErrCnt = AR_PHY_COUNTMAX; |
942 | } |
943 | ahp->ah_stats.ast_ani_cckerrs += |
944 | cckPhyErrCnt - aniState->cckPhyErrCount; |
945 | aniState->cckPhyErrCount = cckPhyErrCnt; |
946 | } |
947 | |
948 | /* |
949 | * Do periodic processing. This routine is called from the |
950 | * driver's rx interrupt handler after processing frames. |
951 | */ |
952 | void |
953 | ar5212AniPoll(struct ath_hal *ah, const HAL_NODE_STATS *stats, |
954 | HAL_CHANNEL *chan) |
955 | { |
956 | struct ath_hal_5212 *ahp = AH5212(ah); |
957 | struct ar5212AniState *aniState = ahp->ah_curani; |
958 | const struct ar5212AniParams *params; |
959 | int32_t listenTime; |
960 | |
961 | ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; |
962 | |
963 | /* XXX can aniState be null? */ |
964 | if (aniState == AH_NULL) |
965 | return; |
966 | if (!ANI_ENA(ah)) |
967 | return; |
968 | |
969 | listenTime = ar5212AniGetListenTime(ah); |
970 | if (listenTime < 0) { |
971 | ahp->ah_stats.ast_ani_lneg++; |
972 | /* restart ANI period if listenTime is invalid */ |
973 | ar5212AniRestart(ah, aniState); |
974 | } |
975 | /* XXX beware of overflow? */ |
976 | aniState->listenTime += listenTime; |
977 | |
978 | OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); |
979 | |
980 | params = aniState->params; |
981 | if (aniState->listenTime > 5*params->period) { |
982 | /* |
983 | * Check to see if need to lower immunity if |
984 | * 5 aniPeriods have passed |
985 | */ |
986 | if (ahp->ah_hasHwPhyCounters) |
987 | updateMIBStats(ah, aniState); |
988 | if (aniState->ofdmPhyErrCount <= aniState->listenTime * |
989 | params->ofdmTrigLow/1000 && |
990 | aniState->cckPhyErrCount <= aniState->listenTime * |
991 | params->cckTrigLow/1000) |
992 | ar5212AniLowerImmunity(ah); |
993 | ar5212AniRestart(ah, aniState); |
994 | } else if (aniState->listenTime > params->period) { |
995 | if (ahp->ah_hasHwPhyCounters) |
996 | updateMIBStats(ah, aniState); |
997 | /* check to see if need to raise immunity */ |
998 | if (aniState->ofdmPhyErrCount > aniState->listenTime * |
999 | params->ofdmTrigHigh / 1000) { |
1000 | HALDEBUG(ah, HAL_DEBUG_ANI, |
1001 | "%s: OFDM err %u listenTime %u\n" , __func__, |
1002 | aniState->ofdmPhyErrCount, aniState->listenTime); |
1003 | ar5212AniOfdmErrTrigger(ah); |
1004 | ar5212AniRestart(ah, aniState); |
1005 | } else if (aniState->cckPhyErrCount > aniState->listenTime * |
1006 | params->cckTrigHigh / 1000) { |
1007 | HALDEBUG(ah, HAL_DEBUG_ANI, |
1008 | "%s: CCK err %u listenTime %u\n" , __func__, |
1009 | aniState->cckPhyErrCount, aniState->listenTime); |
1010 | ar5212AniCckErrTrigger(ah); |
1011 | ar5212AniRestart(ah, aniState); |
1012 | } |
1013 | } |
1014 | } |
1015 | |