/** * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters * @ah: The &struct ath5k_hw * * Just read & reset the registers quickly, so they don't generate more * interrupts, save the counters and schedule the tasklet to decide whether * to raise immunity or not. * * We just need to handle PHY error counters, ath5k_hw_update_mib_counters() * should take care of all "normal" MIB interrupts. */ void ath5k_ani_mib_intr(struct ath5k_hw *ah) { struct ath5k_ani_state *as = &ah->ani_state; /* nothing to do here if HW does not have PHY error counters - they * can't be the reason for the MIB interrupt then */ if (!ah->ah_capabilities.cap_has_phyerr_counters) return; /* not in use but clear anyways */ ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) return; /* If one of the errors triggered, we can get a superfluous second * interrupt, even though we have already reset the register. The * function detects that so we can return early. */ if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) return; if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH || as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) tasklet_schedule(&ah->ani_tasklet); }
/** * ath5k_ani_calibration() - The main ANI calibration function * @ah: The &struct ath5k_hw * * We count OFDM and CCK errors relative to the time where we did not send or * receive ("listen" time) and raise or lower immunity accordingly. * This is called regularly (every second) from the calibration timer, but also * when an error threshold has been reached. * * In order to synchronize access from different contexts, this should be * called only indirectly by scheduling the ANI tasklet! */ void ath5k_ani_calibration(struct ath5k_hw *ah) { struct ath5k_ani_state *as = &ah->ani_state; int listen, ofdm_high, ofdm_low, cck_high, cck_low; /* get listen time since last call and add it to the counter because we * might not have restarted the "ani period" last time. * always do this to calculate the busy time also in manual mode */ listen = ath5k_hw_ani_get_listen_time(ah, as); as->listen_time += listen; if (as->ani_mode != ATH5K_ANI_MODE_AUTO) return; ath5k_ani_save_and_clear_phy_errors(ah, as); ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000; cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000; ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000; cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "listen %d (now %d)", as->listen_time, listen); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "check high ofdm %d/%d cck %d/%d", as->ofdm_errors, ofdm_high, as->cck_errors, cck_high); if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) { /* too many PHY errors - we have to raise immunity */ bool ofdm_flag = as->ofdm_errors > ofdm_high ? true : false; ath5k_ani_raise_immunity(ah, as, ofdm_flag); ath5k_ani_period_restart(as); } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) { /* If more than 5 (TODO: why 5?) periods have passed and we got * relatively little errors we can try to lower immunity */ ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "check low ofdm %d/%d cck %d/%d", as->ofdm_errors, ofdm_low, as->cck_errors, cck_low); if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low) ath5k_ani_lower_immunity(ah, as); ath5k_ani_period_restart(as); } }
void ath5k_ani_calibration(struct ath5k_hw *ah) { struct ath5k_ani_state *as = &ah->ani_state; int listen, ofdm_high, ofdm_low, cck_high, cck_low; listen = ath5k_hw_ani_get_listen_time(ah, as); as->listen_time += listen; if (as->ani_mode != ATH5K_ANI_MODE_AUTO) return; ath5k_ani_save_and_clear_phy_errors(ah, as); ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000; cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000; ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000; cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000; ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "listen %d (now %d)", as->listen_time, listen); ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "check high ofdm %d/%d cck %d/%d", as->ofdm_errors, ofdm_high, as->cck_errors, cck_high); if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) { bool ofdm_flag = as->ofdm_errors > ofdm_high ? true : false; ath5k_ani_raise_immunity(ah, as, ofdm_flag); ath5k_ani_period_restart(as); } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) { ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "check low ofdm %d/%d cck %d/%d", as->ofdm_errors, ofdm_low, as->cck_errors, cck_low); if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low) ath5k_ani_lower_immunity(ah, as); ath5k_ani_period_restart(as); } }
void ath5k_ani_mib_intr(struct ath5k_hw *ah) { struct ath5k_ani_state *as = &ah->ani_state; if (!ah->ah_capabilities.cap_has_phyerr_counters) return; ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) return; if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) return; if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH || as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) tasklet_schedule(&ah->ani_tasklet); }