/****************************************************************************** * * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, * USA * * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Linux Wireless <ilw@linux.intel.com> * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-4965-calib.h" #include "iwl-sta.h" #include "iwl-io.h" #include "iwl-helpers.h" #include "iwl-4965-hw.h" #include "iwl-4965.h" void iwl4965_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_missed_beacon_notif *missed_beacon; missed_beacon = &pkt->u.missed_beacon; if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > priv->missed_beacon_threshold) { IWL_DEBUG_CALIB(priv, "missed bcn cnsq %d totl %d rcd %d expctd %d\n", le32_to_cpu(missed_beacon->consecutive_missed_beacons), le32_to_cpu(missed_beacon->total_missed_becons), le32_to_cpu(missed_beacon->num_recvd_beacons), le32_to_cpu(missed_beacon->num_expected_beacons)); if (!test_bit(STATUS_SCANNING, &priv->status)) iwl4965_init_sensitivity(priv); } } /* Calculate noise level, based on measurements during network silence just * before arriving beacon. This measurement can be done only if we know * exactly when to expect beacons, therefore only when we're associated. */ static void iwl4965_rx_calc_noise(struct iwl_priv *priv) { struct statistics_rx_non_phy *rx_info; int num_active_rx = 0; int total_silence = 0; int bcn_silence_a, bcn_silence_b, bcn_silence_c; int last_rx_noise; rx_info = &(priv->_4965.statistics.rx.general); bcn_silence_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; bcn_silence_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; bcn_silence_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; if (bcn_silence_a) { total_silence += bcn_silence_a; num_active_rx++; } if (bcn_silence_b) { total_silence += bcn_silence_b; num_active_rx++; } if (bcn_silence_c) { total_silence += bcn_silence_c; num_active_rx++; } /* Average among active antennas */ if (num_active_rx) last_rx_noise = (total_silence / num_active_rx) - 107; else last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a, bcn_silence_b, bcn_silence_c, last_rx_noise); } #ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS /* * based on the assumption of all statistics counter are in DWORD * FIXME: This function is for debugging, do not deal with * the case of counters roll-over. */ static void iwl4965_accumulative_statistics(struct iwl_priv *priv, __le32 *stats) { int i, size; __le32 *prev_stats; u32 *accum_stats; u32 *delta, *max_delta; struct statistics_general_common *general, *accum_general; struct statistics_tx *tx, *accum_tx; prev_stats = (__le32 *)&priv->_4965.statistics; accum_stats = (u32 *)&priv->_4965.accum_statistics; size = sizeof(struct iwl_notif_statistics); general = &priv->_4965.statistics.general.common; accum_general = &priv->_4965.accum_statistics.general.common; tx = &priv->_4965.statistics.tx; accum_tx = &priv->_4965.accum_statistics.tx; delta = (u32 *)&priv->_4965.delta_statistics; max_delta = (u32 *)&priv->_4965.max_delta; for (i = sizeof(__le32); i < size; i += sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, accum_stats++) { if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { *delta = (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); *accum_stats += *delta; if (*delta > *max_delta) *max_delta = *delta; } } /* reset accumulative statistics for "no-counter" type statistics */ accum_general->temperature = general->temperature; accum_general->ttl_timestamp = general->ttl_timestamp; } #endif #define REG_RECALIB_PERIOD (60) /** * iwl4965_good_plcp_health - checks for plcp error. * * When the plcp error is exceeding the thresholds, reset the radio * to improve the throughput. */ bool iwl4965_good_plcp_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt) { bool rc = true; int combined_plcp_delta; unsigned int plcp_msec; unsigned long plcp_received_jiffies; if (priv->cfg->base_params->plcp_delta_threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); return rc; } /* * check for plcp_err and trigger radio reset if it exceeds * the plcp error threshold plcp_delta. */ plcp_received_jiffies = jiffies; plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies - (long) priv->plcp_jiffies); priv->plcp_jiffies = plcp_received_jiffies; /* * check to make sure plcp_msec is not 0 to prevent division * by zero. */ if (plcp_msec) { struct statistics_rx_phy *ofdm; struct statistics_rx_ht_phy *ofdm_ht; ofdm = &pkt->u.stats.rx.ofdm; ofdm_ht = &pkt->u.stats.rx.ofdm_ht; combined_plcp_delta = (le32_to_cpu(ofdm->plcp_err) - le32_to_cpu(priv->_4965.statistics. rx.ofdm.plcp_err)) + (le32_to_cpu(ofdm_ht->plcp_err) - le32_to_cpu(priv->_4965.statistics. rx.ofdm_ht.plcp_err)); if ((combined_plcp_delta > 0) && ((combined_plcp_delta * 100) / plcp_msec) > priv->cfg->base_params->plcp_delta_threshold) { /* * if plcp_err exceed the threshold, * the following data is printed in csv format: * Text: plcp_err exceeded %d, * Received ofdm.plcp_err, * Current ofdm.plcp_err, * Received ofdm_ht.plcp_err, * Current ofdm_ht.plcp_err, * combined_plcp_delta, * plcp_msec */ IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, " "%u, %u, %u, %u, %d, %u mSecs\n", priv->cfg->base_params->plcp_delta_threshold, le32_to_cpu(ofdm->plcp_err), le32_to_cpu(ofdm->plcp_err), le32_to_cpu(ofdm_ht->plcp_err), le32_to_cpu(ofdm_ht->plcp_err), combined_plcp_delta, plcp_msec); rc = false; } } return rc; } void iwl4965_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { int change; struct iwl_rx_packet *pkt = rxb_addr(rxb); IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", (int)sizeof(struct iwl_notif_statistics), le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); change = ((priv->_4965.statistics.general.common.temperature != pkt->u.stats.general.common.temperature) || ((priv->_4965.statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) != (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); #ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS iwl4965_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); #endif iwl_legacy_recover_from_statistics(priv, pkt); memcpy(&priv->_4965.statistics, &pkt->u.stats, sizeof(priv->_4965.statistics)); set_bit(STATUS_STATISTICS, &priv->status); /* Reschedule the statistics timer to occur in * REG_RECALIB_PERIOD seconds to ensure we get a * thermal update even if the uCode doesn't give * us one */ mod_timer(&priv->statistics_periodic, jiffies + msecs_to_jiffies(REG_RECALIB_PERIOD * 1000)); if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { iwl4965_rx_calc_noise(priv); queue_work(priv->workqueue, &priv->run_time_calib_work); } if (priv->cfg->ops->lib->temp_ops.temperature && change) priv->cfg->ops->lib->temp_ops.temperature(priv); } void iwl4965_reply_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) { #ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS memset(&priv->_4965.accum_statistics, 0, sizeof(struct iwl_notif_statistics)); memset(&priv->_4965.delta_statistics, 0, sizeof(struct iwl_notif_statistics)); memset(&priv->_4965.max_delta, 0, sizeof(struct iwl_notif_statistics)); #endif IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); } iwl4965_rx_statistics(priv, rxb); }