Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ani.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010 Bruno Randolf <[email protected]>
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18 
19 #include "ath5k.h"
20 #include "reg.h"
21 #include "debug.h"
22 #include "ani.h"
23 
56 /***********************\
57 * ANI parameter control *
58 \***********************/
59 
65 void
67 {
68  /* TODO:
69  * ANI documents suggest the following five levels to use, but the HAL
70  * and ath9k use only the last two levels, making this
71  * essentially an on/off option. There *may* be a reason for this (???),
72  * so i stick with the HAL version for now...
73  */
74 #if 0
75  static const s8 lo[] = { -52, -56, -60, -64, -70 };
76  static const s8 hi[] = { -18, -18, -16, -14, -12 };
77  static const s8 sz[] = { -34, -41, -48, -55, -62 };
78  static const s8 fr[] = { -70, -72, -75, -78, -80 };
79 #else
80  static const s8 lo[] = { -64, -70 };
81  static const s8 hi[] = { -14, -12 };
82  static const s8 sz[] = { -55, -62 };
83  static const s8 fr[] = { -78, -80 };
84 #endif
85  if (level < 0 || level >= ARRAY_SIZE(sz)) {
86  ATH5K_ERR(ah, "noise immunity level %d out of range",
87  level);
88  return;
89  }
90 
92  AR5K_PHY_DESIRED_SIZE_TOT, sz[level]);
94  AR5K_PHY_AGCCOARSE_LO, lo[level]);
96  AR5K_PHY_AGCCOARSE_HI, hi[level]);
98  AR5K_PHY_SIG_FIRPWR, fr[level]);
99 
100  ah->ani_state.noise_imm_level = level;
101  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
102 }
103 
110 void
112 {
113  static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
114 
115  if (level < 0 || level >= ARRAY_SIZE(val) ||
116  level > ah->ani_state.max_spur_level) {
117  ATH5K_ERR(ah, "spur immunity level %d out of range",
118  level);
119  return;
120  }
121 
124 
125  ah->ani_state.spur_level = level;
126  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
127 }
128 
134 void
136 {
137  static const int val[] = { 0, 4, 8 };
138 
139  if (level < 0 || level >= ARRAY_SIZE(val)) {
140  ATH5K_ERR(ah, "firstep level %d out of range", level);
141  return;
142  }
143 
145  AR5K_PHY_SIG_FIRSTEP, val[level]);
146 
147  ah->ani_state.firstep_level = level;
148  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
149 }
150 
156 void
158 {
159  static const int m1l[] = { 127, 50 };
160  static const int m2l[] = { 127, 40 };
161  static const int m1[] = { 127, 0x4d };
162  static const int m2[] = { 127, 0x40 };
163  static const int m2cnt[] = { 31, 16 };
164  static const int m2lcnt[] = { 63, 48 };
165 
178 
179  if (on)
182  else
185 
186  ah->ani_state.ofdm_weak_sig = on;
187  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
188  on ? "on" : "off");
189 }
190 
196 void
198 {
199  static const int val[] = { 8, 6 };
202  ah->ani_state.cck_weak_sig = on;
203  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
204  on ? "on" : "off");
205 }
206 
207 
208 /***************\
209 * ANI algorithm *
210 \***************/
211 
222 static void
223 ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
224  bool ofdm_trigger)
225 {
226  int rssi = ewma_read(&ah->ah_beacon_rssi_avg);
227 
228  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)",
229  ofdm_trigger ? "ODFM" : "CCK");
230 
231  /* first: raise noise immunity */
234  return;
235  }
236 
237  /* only OFDM: raise spur immunity level */
238  if (ofdm_trigger &&
239  as->spur_level < ah->ani_state.max_spur_level) {
241  return;
242  }
243 
244  /* AP mode */
245  if (ah->opmode == NL80211_IFTYPE_AP) {
248  return;
249  }
250 
251  /* STA and IBSS mode */
252 
253  /* TODO: for IBSS mode it would be better to keep a beacon RSSI average
254  * per each neighbour node and use the minimum of these, to make sure we
255  * don't shut out a remote node by raising immunity too high. */
256 
257  if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
258  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
259  "beacon RSSI high");
260  /* only OFDM: beacon RSSI is high, we can disable ODFM weak
261  * signal detection */
262  if (ofdm_trigger && as->ofdm_weak_sig) {
265  return;
266  }
267  /* as a last resort or CCK: raise firstep level */
270  return;
271  }
272  } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
273  /* beacon RSSI in mid range, we need OFDM weak signal detect,
274  * but can raise firstep level */
275  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
276  "beacon RSSI mid");
277  if (ofdm_trigger && !as->ofdm_weak_sig)
281  return;
282  } else if (ah->ah_current_channel->band == IEEE80211_BAND_2GHZ) {
283  /* beacon RSSI is low. in B/G mode turn of OFDM weak signal
284  * detect and zero firstep level to maximize CCK sensitivity */
285  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
286  "beacon RSSI low, 2GHz");
287  if (ofdm_trigger && as->ofdm_weak_sig)
289  if (as->firstep_level > 0)
291  return;
292  }
293 
294  /* TODO: why not?:
295  if (as->cck_weak_sig == true) {
296  ath5k_ani_set_cck_weak_signal_detection(ah, false);
297  }
298  */
299 }
300 
309 static void
310 ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as)
311 {
312  int rssi = ewma_read(&ah->ah_beacon_rssi_avg);
313 
314  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity");
315 
316  if (ah->opmode == NL80211_IFTYPE_AP) {
317  /* AP mode */
318  if (as->firstep_level > 0) {
320  return;
321  }
322  } else {
323  /* STA and IBSS mode (see TODO above) */
324  if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
325  /* beacon signal is high, leave OFDM weak signal
326  * detection off or it may oscillate
327  * TODO: who said it's off??? */
328  } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
329  /* beacon RSSI is mid-range: turn on ODFM weak signal
330  * detection and next, lower firstep level */
331  if (!as->ofdm_weak_sig) {
333  true);
334  return;
335  }
336  if (as->firstep_level > 0) {
338  as->firstep_level - 1);
339  return;
340  }
341  } else {
342  /* beacon signal is low: only reduce firstep level */
343  if (as->firstep_level > 0) {
345  as->firstep_level - 1);
346  return;
347  }
348  }
349  }
350 
351  /* all modes */
352  if (as->spur_level > 0) {
354  return;
355  }
356 
357  /* finally, reduce noise immunity */
358  if (as->noise_imm_level > 0) {
360  return;
361  }
362 }
363 
373 static int
374 ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
375 {
376  struct ath_common *common = ath5k_hw_common(ah);
377  int listen;
378 
379  spin_lock_bh(&common->cc_lock);
380 
382  memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
383 
384  /* clears common->cc_ani */
385  listen = ath_hw_get_listen_time(common);
386 
387  spin_unlock_bh(&common->cc_lock);
388 
389  return listen;
390 }
391 
405 static int
406 ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah,
407  struct ath5k_ani_state *as)
408 {
409  unsigned int ofdm_err, cck_err;
410 
411  if (!ah->ah_capabilities.cap_has_phyerr_counters)
412  return 0;
413 
414  ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1);
415  cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2);
416 
417  /* reset counters first, we might be in a hurry (interrupt) */
418  ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
420  ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
422 
423  ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err);
424  cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err);
425 
426  /* sometimes both can be zero, especially when there is a superfluous
427  * second interrupt. detect that here and return an error. */
428  if (ofdm_err <= 0 && cck_err <= 0)
429  return 0;
430 
431  /* avoid negative values should one of the registers overflow */
432  if (ofdm_err > 0) {
433  as->ofdm_errors += ofdm_err;
434  as->sum_ofdm_errors += ofdm_err;
435  }
436  if (cck_err > 0) {
437  as->cck_errors += cck_err;
438  as->sum_cck_errors += cck_err;
439  }
440  return 1;
441 }
442 
449 static void
450 ath5k_ani_period_restart(struct ath5k_ani_state *as)
451 {
452  /* keep last values for debugging */
453  as->last_ofdm_errors = as->ofdm_errors;
454  as->last_cck_errors = as->cck_errors;
455  as->last_listen = as->listen_time;
456 
457  as->ofdm_errors = 0;
458  as->cck_errors = 0;
459  as->listen_time = 0;
460 }
461 
474 void
476 {
477  struct ath5k_ani_state *as = &ah->ani_state;
478  int listen, ofdm_high, ofdm_low, cck_high, cck_low;
479 
480  /* get listen time since last call and add it to the counter because we
481  * might not have restarted the "ani period" last time.
482  * always do this to calculate the busy time also in manual mode */
483  listen = ath5k_hw_ani_get_listen_time(ah, as);
484  as->listen_time += listen;
485 
486  if (as->ani_mode != ATH5K_ANI_MODE_AUTO)
487  return;
488 
489  ath5k_ani_save_and_clear_phy_errors(ah, as);
490 
491  ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000;
492  cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000;
493  ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000;
494  cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000;
495 
496  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
497  "listen %d (now %d)", as->listen_time, listen);
498  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
499  "check high ofdm %d/%d cck %d/%d",
500  as->ofdm_errors, ofdm_high, as->cck_errors, cck_high);
501 
502  if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) {
503  /* too many PHY errors - we have to raise immunity */
504  bool ofdm_flag = as->ofdm_errors > ofdm_high ? true : false;
505  ath5k_ani_raise_immunity(ah, as, ofdm_flag);
506  ath5k_ani_period_restart(as);
507 
508  } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) {
509  /* If more than 5 (TODO: why 5?) periods have passed and we got
510  * relatively little errors we can try to lower immunity */
511  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
512  "check low ofdm %d/%d cck %d/%d",
513  as->ofdm_errors, ofdm_low, as->cck_errors, cck_low);
514 
515  if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low)
516  ath5k_ani_lower_immunity(ah, as);
517 
518  ath5k_ani_period_restart(as);
519  }
520 }
521 
522 
523 /*******************\
524 * Interrupt handler *
525 \*******************/
526 
538 void
540 {
541  struct ath5k_ani_state *as = &ah->ani_state;
542 
543  /* nothing to do here if HW does not have PHY error counters - they
544  * can't be the reason for the MIB interrupt then */
545  if (!ah->ah_capabilities.cap_has_phyerr_counters)
546  return;
547 
548  /* not in use but clear anyways */
549  ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
550  ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
551 
552  if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
553  return;
554 
555  /* If one of the errors triggered, we can get a superfluous second
556  * interrupt, even though we have already reset the register. The
557  * function detects that so we can return early. */
558  if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0)
559  return;
560 
563  tasklet_schedule(&ah->ani_tasklet);
564 }
565 
575 void
577  enum ath5k_phy_error_code phyerr)
578 {
579  struct ath5k_ani_state *as = &ah->ani_state;
580 
581  if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) {
582  as->ofdm_errors++;
584  tasklet_schedule(&ah->ani_tasklet);
585  } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) {
586  as->cck_errors++;
588  tasklet_schedule(&ah->ani_tasklet);
589  }
590 }
591 
592 
593 /****************\
594 * Initialization *
595 \****************/
596 
603 static void
604 ath5k_enable_phy_err_counters(struct ath5k_hw *ah)
605 {
606  ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
608  ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
610  ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK);
611  ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK);
612 
613  /* not in use */
614  ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
615  ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
616 }
617 
624 static void
625 ath5k_disable_phy_err_counters(struct ath5k_hw *ah)
626 {
627  ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1);
628  ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2);
629  ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK);
630  ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK);
631 
632  /* not in use */
633  ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
634  ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
635 }
636 
644 void
646 {
647  /* ANI is only possible on 5212 and newer */
648  if (ah->ah_version < AR5K_AR5212)
649  return;
650 
651  if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) {
652  ATH5K_ERR(ah, "ANI mode %d out of range", mode);
653  return;
654  }
655 
656  /* clear old state information */
657  memset(&ah->ani_state, 0, sizeof(ah->ani_state));
658 
659  /* older hardware has more spur levels than newer */
660  if (ah->ah_mac_srev < AR5K_SREV_AR2414)
661  ah->ani_state.max_spur_level = 7;
662  else
663  ah->ani_state.max_spur_level = 2;
664 
665  /* initial values for our ani parameters */
666  if (mode == ATH5K_ANI_MODE_OFF) {
667  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n");
668  } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) {
669  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
670  "ANI manual low -> high sensitivity\n");
676  } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) {
677  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
678  "ANI manual high -> low sensitivity\n");
682  ah->ani_state.max_spur_level);
686  } else if (mode == ATH5K_ANI_MODE_AUTO) {
687  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n");
693  }
694 
695  /* newer hardware has PHY error counter registers which we can use to
696  * get OFDM and CCK error counts. older hardware has to set rxfilter and
697  * report every single PHY error by calling ath5k_ani_phy_error_report()
698  */
699  if (mode == ATH5K_ANI_MODE_AUTO) {
700  if (ah->ah_capabilities.cap_has_phyerr_counters)
701  ath5k_enable_phy_err_counters(ah);
702  else
705  } else {
706  if (ah->ah_capabilities.cap_has_phyerr_counters)
707  ath5k_disable_phy_err_counters(ah);
708  else
711  }
712 
713  ah->ani_state.ani_mode = mode;
714 }
715 
716 
717 /**************\
718 * Debug output *
719 \**************/
720 
721 #ifdef CONFIG_ATH5K_DEBUG
722 
729 void
731 {
732  /* clears too */
733  pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL));
734  pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL));
735  pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK));
736  pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL));
737 
738  /* no clear */
739  pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX));
740  pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX));
741  pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR));
742  pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE));
743 
744  pr_notice("AR5K_PHYERR_CNT1\t%d\n",
745  ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1));
746  pr_notice("AR5K_PHYERR_CNT2\t%d\n",
747  ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2));
748  pr_notice("AR5K_OFDM_FIL_CNT\t%d\n",
749  ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT));
750  pr_notice("AR5K_CCK_FIL_CNT\t%d\n",
751  ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT));
752 }
753 
754 #endif