/****************************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * 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 * * BSD LICENSE * * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/* "false alarms" are signals that our DSP tries to lock onto, * but then determines that they are either noise, or transmissions * from a distant wireless network (also "noise", really) that get * "stepped on" by stronger transmissions within our own network. * This algorithm attempts to set a sensitivity level that is high * enough to receive all of our own network traffic, but not so * high that our DSP gets too busy trying to lock onto non-network
* activity/noise. */ staticint
il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time, struct stats_general_data *rx_info)
{
u32 max_nrg_cck = 0; int i = 0;
u8 max_silence_rssi = 0;
u32 silence_ref = 0;
u8 silence_rssi_a = 0;
u8 silence_rssi_b = 0;
u8 silence_rssi_c = 0;
u32 val;
/* "false_alarms" values below are cross-multiplications to assess the * numbers of false alarms within the measured period of actual Rx * (Rx is off when we're txing), vs the min/max expected false alarms * (some should be expected if rx is sensitive enough) in a * hypothetical listening period of 200 time units (TU), 204.8 msec: * * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time *
* */
u32 false_alarms = norm_fa * 200 * 1024;
u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; struct il_sensitivity_data *data = NULL; conststruct il_sensitivity_ranges *ranges = il->hw_params.sens;
data = &(il->sensitivity_data);
data->nrg_auto_corr_silence_diff = 0;
/* Find max silence rssi among all 3 receivers. * This is background noise, which may include transmissions from other
* networks, measured during silence before our network's beacon */
silence_rssi_a =
(u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8);
silence_rssi_b =
(u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8);
silence_rssi_c =
(u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8);
val = max(silence_rssi_b, silence_rssi_c);
max_silence_rssi = max(silence_rssi_a, (u8) val);
/* Store silence rssi in 20-beacon history table */
data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
data->nrg_silence_idx++; if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
data->nrg_silence_idx = 0;
/* Find max silence rssi across 20 beacon history */ for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
val = data->nrg_silence_rssi[i];
silence_ref = max(silence_ref, val);
}
D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a,
silence_rssi_b, silence_rssi_c, silence_ref);
/* Find max rx energy (min value!) among all 3 receivers, * measured during beacon frame.
* Save it in 10-beacon history table. */
i = data->nrg_energy_idx;
val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
data->nrg_energy_idx++; if (data->nrg_energy_idx >= 10)
data->nrg_energy_idx = 0;
/* Find min rx energy (max value) across 10 beacon history. * This is the minimum signal level that we want to receive well. * Add backoff (margin so we don't miss slightly lower energy frames).
* This establishes an upper bound (min value) for energy threshold. */
max_nrg_cck = data->nrg_value[0]; for (i = 1; i < 10; i++)
max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
max_nrg_cck += 6;
D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
rx_info->beacon_energy_a, rx_info->beacon_energy_b,
rx_info->beacon_energy_c, max_nrg_cck - 6);
/* Count number of consecutive beacons with fewer-than-desired
* false alarms. */ if (false_alarms < min_false_alarms)
data->num_in_cck_no_fa++; else
data->num_in_cck_no_fa = 0;
D_CALIB("consecutive bcns with few false alarms = %u\n",
data->num_in_cck_no_fa);
/* If we got too many false alarms this time, reduce sensitivity */ if (false_alarms > max_false_alarms &&
data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) {
D_CALIB("norm FA %u > max FA %u\n", false_alarms,
max_false_alarms);
D_CALIB("... reducing sensitivity\n");
data->nrg_curr_state = IL_FA_TOO_MANY; /* Store for "fewer than desired" on later beacon */
data->nrg_silence_ref = silence_ref;
/* increase energy threshold (reduce nrg value)
* to decrease sensitivity */
data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; /* Else if we got fewer than desired, increase sensitivity */
} elseif (false_alarms < min_false_alarms) {
data->nrg_curr_state = IL_FA_TOO_FEW;
/* Compare silence level with silence level for most recent
* healthy number or too many false alarms */
data->nrg_auto_corr_silence_diff =
(s32) data->nrg_silence_ref - (s32) silence_ref;
D_CALIB("norm FA %u < min FA %u, silence diff %d\n",
false_alarms, min_false_alarms,
data->nrg_auto_corr_silence_diff);
/* Increase value to increase sensitivity, but only if: * 1a) previous beacon did *not* have *too many* false alarms * 1b) AND there's a significant difference in Rx levels * from a previous beacon with too many, or healthy # FAs * OR 2) We've seen a lot of beacons (100) with too few
* false alarms */ if (data->nrg_prev_state != IL_FA_TOO_MANY &&
(data->nrg_auto_corr_silence_diff > NRG_DIFF ||
data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
D_CALIB("... increasing sensitivity\n"); /* Increase nrg value to increase sensitivity */
val = data->nrg_th_cck + NRG_STEP_CCK;
data->nrg_th_cck = min((u32) ranges->min_nrg_cck, val);
} else {
D_CALIB("... but not changing sensitivity\n");
}
/* Else we got a healthy number of false alarms, keep status quo */
} else {
D_CALIB(" FA in safe zone\n");
data->nrg_curr_state = IL_FA_GOOD_RANGE;
/* Store for use in "fewer than desired" with later beacon */
data->nrg_silence_ref = silence_ref;
/* If previous beacon had too many false alarms, * give it some extra margin by reducing sensitivity again
* (but don't go below measured energy of desired Rx) */ if (IL_FA_TOO_MANY == data->nrg_prev_state) {
D_CALIB("... increasing margin\n"); if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
data->nrg_th_cck -= NRG_MARGIN; else
data->nrg_th_cck = max_nrg_cck;
}
}
/* Make sure the energy threshold does not go above the measured * energy of the desired Rx signals (reduced by backoff margin), * or else we might start missing Rx frames. * Lower value is higher energy, so we use max()!
*/
data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
D_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck);
data->nrg_prev_state = data->nrg_curr_state;
/* Auto-correlation CCK algorithm */ if (false_alarms > min_false_alarms) {
/* increase auto_corr values to decrease sensitivity * so the DSP won't be disturbed by the noise
*/ if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; else {
val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
data->auto_corr_cck =
min((u32) ranges->auto_corr_max_cck, val);
}
val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
data->auto_corr_cck_mrc =
min((u32) ranges->auto_corr_max_cck_mrc, val);
} elseif (false_alarms < min_false_alarms &&
(data->nrg_auto_corr_silence_diff > NRG_DIFF ||
data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
/* Decrease auto_corr values to increase sensitivity */
val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val);
val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
data->auto_corr_cck_mrc =
max((u32) ranges->auto_corr_min_cck_mrc, val);
}
if (!rx_enable_time) {
D_CALIB("<< RX Enable Time == 0!\n"); return;
}
/* These stats increase monotonically, and do not reset * at each beacon. Calculate difference from last value, or just
* use the new stats value if it has reset or wrapped around. */ if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
data->last_bad_plcp_cnt_cck = bad_plcp_cck; else {
bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
data->last_bad_plcp_cnt_cck += bad_plcp_cck;
}
D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1],
average_sig[2]);
D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig,
max_average_sig_antenna_i);
/* Compare signal strengths for all 3 receivers. */ for (i = 0; i < NUM_RX_CHAINS; i++) { if (i != max_average_sig_antenna_i) {
s32 rssi_delta = (max_average_sig - average_sig[i]);
/* If signal is very weak, compared with
* strongest, mark it as disconnected. */ if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
data->disconn_array[i] = 1; else
active_chains |= (1 << i);
D_CALIB("i = %d rssiDelta = %d " "disconn_array[i] = %d\n", i, rssi_delta,
data->disconn_array[i]);
}
}
/* * The above algorithm sometimes fails when the ucode * reports 0 for all chains. It's not clear why that * happens to start with, but it is then causing trouble * because this can make us enable more chains than the * hardware really has. * * To be safe, simply mask out any chains that we know * are not on the device.
*/
active_chains &= il->hw_params.valid_rx_ant;
num_tx_chains = 0; for (i = 0; i < NUM_RX_CHAINS; i++) { /* loops on all the bits of
* il->hw_setting.valid_tx_ant */
u8 ant_msk = (1 << i); if (!(il->hw_params.valid_tx_ant & ant_msk)) continue;
num_tx_chains++; if (data->disconn_array[i] == 0) /* there is a Tx antenna connected */ break; if (num_tx_chains == il->hw_params.tx_chains_num &&
data->disconn_array[i]) { /* * If all chains are disconnected * connect the first valid tx chain
*/
first_chain =
il4965_find_first_chain(il->cfg->valid_tx_ant);
data->disconn_array[first_chain] = 0;
active_chains |= BIT(first_chain);
D_CALIB("All Tx chains are disconnected" "- declare %d as connected\n", first_chain); break;
}
}
if (active_chains != il->hw_params.valid_rx_ant &&
active_chains != il->chain_noise_data.active_chains)
D_CALIB("Detected that not all antennas are connected! " "Connected: %#x, valid: %#x.\n", active_chains,
il->hw_params.valid_rx_ant);
/* Save for use within RXON, TX, SCAN commands, etc. */
data->active_chains = active_chains;
D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains);
}
/* TODO we might want recalculate
* rx_chain in rxon cmd */
/* Mark so we run this algo only once! */
data->state = IL_CHAIN_NOISE_CALIBRATED;
}
}
/* * Accumulate 16 beacons of signal and noise stats for each of * 3 receivers/antennas/rx-chains, then figure out: * 1) Which antennas are connected. * 2) Differential rx gain settings to balance the 3 receivers.
*/ void
il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp)
{ struct il_chain_noise_data *data = NULL;
/* * Accumulate just the first "chain_noise_num_beacons" after * the first association, then we're done forever.
*/ if (data->state != IL_CHAIN_NOISE_ACCUMULATE) { if (data->state == IL_CHAIN_NOISE_ALIVE)
D_CALIB("Wait for noise calib reset\n"); return;
}
/* Make sure we accumulate data for just the associated channel
* (even if scanning). */ if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) {
D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum,
rxon_band24);
spin_unlock_irqrestore(&il->lock, flags); return;
}
D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24,
data->beacon_count);
D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b,
chain_sig_c);
D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b,
chain_noise_c);
/* If this is the "chain_noise_num_beacons", determine: * 1) Disconnected antennas (using signal strengths)
* 2) Differential gain (using silence noise) to balance receivers */ if (data->beacon_count != il->cfg->chain_noise_num_beacons) return;
/* Analyze signal for disconnected antenna */
il4965_find_disconn_antenna(il, average_sig, data);
for (i = 0; i < NUM_RX_CHAINS; i++) { if (!data->disconn_array[i] &&
average_noise[i] <= min_average_noise) { /* This means that chain i is active and has
* lower noise values so far: */
min_average_noise = average_noise[i];
min_average_noise_antenna_i = i;
}
}
D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0],
average_noise[1], average_noise[2]);
/* Some power changes may have been made during the calibration. * Update and commit the RXON
*/ if (il->ops->update_chain_flags)
il->ops->update_chain_flags(il);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.