cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

calib.c (34224B)


      1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
      2/*
      3 * Copyright (C) 2005-2014 Intel Corporation
      4 */
      5#include <linux/slab.h>
      6#include <net/mac80211.h>
      7
      8#include "iwl-trans.h"
      9
     10#include "dev.h"
     11#include "calib.h"
     12#include "agn.h"
     13
     14/*****************************************************************************
     15 * INIT calibrations framework
     16 *****************************************************************************/
     17
     18/* Opaque calibration results */
     19struct iwl_calib_result {
     20	struct list_head list;
     21	size_t cmd_len;
     22	struct iwl_calib_hdr hdr;
     23	/* data follows */
     24};
     25
     26struct statistics_general_data {
     27	u32 beacon_silence_rssi_a;
     28	u32 beacon_silence_rssi_b;
     29	u32 beacon_silence_rssi_c;
     30	u32 beacon_energy_a;
     31	u32 beacon_energy_b;
     32	u32 beacon_energy_c;
     33};
     34
     35int iwl_send_calib_results(struct iwl_priv *priv)
     36{
     37	struct iwl_host_cmd hcmd = {
     38		.id = REPLY_PHY_CALIBRATION_CMD,
     39	};
     40	struct iwl_calib_result *res;
     41
     42	list_for_each_entry(res, &priv->calib_results, list) {
     43		int ret;
     44
     45		hcmd.len[0] = res->cmd_len;
     46		hcmd.data[0] = &res->hdr;
     47		hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
     48		ret = iwl_dvm_send_cmd(priv, &hcmd);
     49		if (ret) {
     50			IWL_ERR(priv, "Error %d on calib cmd %d\n",
     51				ret, res->hdr.op_code);
     52			return ret;
     53		}
     54	}
     55
     56	return 0;
     57}
     58
     59int iwl_calib_set(struct iwl_priv *priv,
     60		  const struct iwl_calib_hdr *cmd, int len)
     61{
     62	struct iwl_calib_result *res, *tmp;
     63
     64	res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr),
     65		      GFP_ATOMIC);
     66	if (!res)
     67		return -ENOMEM;
     68	memcpy(&res->hdr, cmd, len);
     69	res->cmd_len = len;
     70
     71	list_for_each_entry(tmp, &priv->calib_results, list) {
     72		if (tmp->hdr.op_code == res->hdr.op_code) {
     73			list_replace(&tmp->list, &res->list);
     74			kfree(tmp);
     75			return 0;
     76		}
     77	}
     78
     79	/* wasn't in list already */
     80	list_add_tail(&res->list, &priv->calib_results);
     81
     82	return 0;
     83}
     84
     85void iwl_calib_free_results(struct iwl_priv *priv)
     86{
     87	struct iwl_calib_result *res, *tmp;
     88
     89	list_for_each_entry_safe(res, tmp, &priv->calib_results, list) {
     90		list_del(&res->list);
     91		kfree(res);
     92	}
     93}
     94
     95/*****************************************************************************
     96 * RUNTIME calibrations framework
     97 *****************************************************************************/
     98
     99/* "false alarms" are signals that our DSP tries to lock onto,
    100 *   but then determines that they are either noise, or transmissions
    101 *   from a distant wireless network (also "noise", really) that get
    102 *   "stepped on" by stronger transmissions within our own network.
    103 * This algorithm attempts to set a sensitivity level that is high
    104 *   enough to receive all of our own network traffic, but not so
    105 *   high that our DSP gets too busy trying to lock onto non-network
    106 *   activity/noise. */
    107static int iwl_sens_energy_cck(struct iwl_priv *priv,
    108				   u32 norm_fa,
    109				   u32 rx_enable_time,
    110				   struct statistics_general_data *rx_info)
    111{
    112	u32 max_nrg_cck = 0;
    113	int i = 0;
    114	u8 max_silence_rssi = 0;
    115	u32 silence_ref = 0;
    116	u8 silence_rssi_a = 0;
    117	u8 silence_rssi_b = 0;
    118	u8 silence_rssi_c = 0;
    119	u32 val;
    120
    121	/* "false_alarms" values below are cross-multiplications to assess the
    122	 *   numbers of false alarms within the measured period of actual Rx
    123	 *   (Rx is off when we're txing), vs the min/max expected false alarms
    124	 *   (some should be expected if rx is sensitive enough) in a
    125	 *   hypothetical listening period of 200 time units (TU), 204.8 msec:
    126	 *
    127	 * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
    128	 *
    129	 * */
    130	u32 false_alarms = norm_fa * 200 * 1024;
    131	u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
    132	u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
    133	struct iwl_sensitivity_data *data = NULL;
    134	const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
    135
    136	data = &(priv->sensitivity_data);
    137
    138	data->nrg_auto_corr_silence_diff = 0;
    139
    140	/* Find max silence rssi among all 3 receivers.
    141	 * This is background noise, which may include transmissions from other
    142	 *    networks, measured during silence before our network's beacon */
    143	silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
    144			    ALL_BAND_FILTER) >> 8);
    145	silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
    146			    ALL_BAND_FILTER) >> 8);
    147	silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
    148			    ALL_BAND_FILTER) >> 8);
    149
    150	val = max(silence_rssi_b, silence_rssi_c);
    151	max_silence_rssi = max(silence_rssi_a, (u8) val);
    152
    153	/* Store silence rssi in 20-beacon history table */
    154	data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
    155	data->nrg_silence_idx++;
    156	if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
    157		data->nrg_silence_idx = 0;
    158
    159	/* Find max silence rssi across 20 beacon history */
    160	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
    161		val = data->nrg_silence_rssi[i];
    162		silence_ref = max(silence_ref, val);
    163	}
    164	IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n",
    165			silence_rssi_a, silence_rssi_b, silence_rssi_c,
    166			silence_ref);
    167
    168	/* Find max rx energy (min value!) among all 3 receivers,
    169	 *   measured during beacon frame.
    170	 * Save it in 10-beacon history table. */
    171	i = data->nrg_energy_idx;
    172	val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
    173	data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
    174
    175	data->nrg_energy_idx++;
    176	if (data->nrg_energy_idx >= 10)
    177		data->nrg_energy_idx = 0;
    178
    179	/* Find min rx energy (max value) across 10 beacon history.
    180	 * This is the minimum signal level that we want to receive well.
    181	 * Add backoff (margin so we don't miss slightly lower energy frames).
    182	 * This establishes an upper bound (min value) for energy threshold. */
    183	max_nrg_cck = data->nrg_value[0];
    184	for (i = 1; i < 10; i++)
    185		max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
    186	max_nrg_cck += 6;
    187
    188	IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
    189			rx_info->beacon_energy_a, rx_info->beacon_energy_b,
    190			rx_info->beacon_energy_c, max_nrg_cck - 6);
    191
    192	/* Count number of consecutive beacons with fewer-than-desired
    193	 *   false alarms. */
    194	if (false_alarms < min_false_alarms)
    195		data->num_in_cck_no_fa++;
    196	else
    197		data->num_in_cck_no_fa = 0;
    198	IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n",
    199			data->num_in_cck_no_fa);
    200
    201	/* If we got too many false alarms this time, reduce sensitivity */
    202	if ((false_alarms > max_false_alarms) &&
    203		(data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) {
    204		IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n",
    205		     false_alarms, max_false_alarms);
    206		IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n");
    207		data->nrg_curr_state = IWL_FA_TOO_MANY;
    208		/* Store for "fewer than desired" on later beacon */
    209		data->nrg_silence_ref = silence_ref;
    210
    211		/* increase energy threshold (reduce nrg value)
    212		 *   to decrease sensitivity */
    213		data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK;
    214	/* Else if we got fewer than desired, increase sensitivity */
    215	} else if (false_alarms < min_false_alarms) {
    216		data->nrg_curr_state = IWL_FA_TOO_FEW;
    217
    218		/* Compare silence level with silence level for most recent
    219		 *   healthy number or too many false alarms */
    220		data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
    221						   (s32)silence_ref;
    222
    223		IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n",
    224			 false_alarms, min_false_alarms,
    225			 data->nrg_auto_corr_silence_diff);
    226
    227		/* Increase value to increase sensitivity, but only if:
    228		 * 1a) previous beacon did *not* have *too many* false alarms
    229		 * 1b) AND there's a significant difference in Rx levels
    230		 *      from a previous beacon with too many, or healthy # FAs
    231		 * OR 2) We've seen a lot of beacons (100) with too few
    232		 *       false alarms */
    233		if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
    234			((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
    235			(data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
    236
    237			IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n");
    238			/* Increase nrg value to increase sensitivity */
    239			val = data->nrg_th_cck + NRG_STEP_CCK;
    240			data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val);
    241		} else {
    242			IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n");
    243		}
    244
    245	/* Else we got a healthy number of false alarms, keep status quo */
    246	} else {
    247		IWL_DEBUG_CALIB(priv, " FA in safe zone\n");
    248		data->nrg_curr_state = IWL_FA_GOOD_RANGE;
    249
    250		/* Store for use in "fewer than desired" with later beacon */
    251		data->nrg_silence_ref = silence_ref;
    252
    253		/* If previous beacon had too many false alarms,
    254		 *   give it some extra margin by reducing sensitivity again
    255		 *   (but don't go below measured energy of desired Rx) */
    256		if (data->nrg_prev_state == IWL_FA_TOO_MANY) {
    257			IWL_DEBUG_CALIB(priv, "... increasing margin\n");
    258			if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
    259				data->nrg_th_cck -= NRG_MARGIN;
    260			else
    261				data->nrg_th_cck = max_nrg_cck;
    262		}
    263	}
    264
    265	/* Make sure the energy threshold does not go above the measured
    266	 * energy of the desired Rx signals (reduced by backoff margin),
    267	 * or else we might start missing Rx frames.
    268	 * Lower value is higher energy, so we use max()!
    269	 */
    270	data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
    271	IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck);
    272
    273	data->nrg_prev_state = data->nrg_curr_state;
    274
    275	/* Auto-correlation CCK algorithm */
    276	if (false_alarms > min_false_alarms) {
    277
    278		/* increase auto_corr values to decrease sensitivity
    279		 * so the DSP won't be disturbed by the noise
    280		 */
    281		if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
    282			data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
    283		else {
    284			val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
    285			data->auto_corr_cck =
    286				min((u32)ranges->auto_corr_max_cck, val);
    287		}
    288		val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
    289		data->auto_corr_cck_mrc =
    290			min((u32)ranges->auto_corr_max_cck_mrc, val);
    291	} else if ((false_alarms < min_false_alarms) &&
    292	   ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
    293	   (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
    294
    295		/* Decrease auto_corr values to increase sensitivity */
    296		val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
    297		data->auto_corr_cck =
    298			max((u32)ranges->auto_corr_min_cck, val);
    299		val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
    300		data->auto_corr_cck_mrc =
    301			max((u32)ranges->auto_corr_min_cck_mrc, val);
    302	}
    303
    304	return 0;
    305}
    306
    307
    308static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
    309				       u32 norm_fa,
    310				       u32 rx_enable_time)
    311{
    312	u32 val;
    313	u32 false_alarms = norm_fa * 200 * 1024;
    314	u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
    315	u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
    316	struct iwl_sensitivity_data *data = NULL;
    317	const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
    318
    319	data = &(priv->sensitivity_data);
    320
    321	/* If we got too many false alarms this time, reduce sensitivity */
    322	if (false_alarms > max_false_alarms) {
    323
    324		IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n",
    325			     false_alarms, max_false_alarms);
    326
    327		val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
    328		data->auto_corr_ofdm =
    329			min((u32)ranges->auto_corr_max_ofdm, val);
    330
    331		val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
    332		data->auto_corr_ofdm_mrc =
    333			min((u32)ranges->auto_corr_max_ofdm_mrc, val);
    334
    335		val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
    336		data->auto_corr_ofdm_x1 =
    337			min((u32)ranges->auto_corr_max_ofdm_x1, val);
    338
    339		val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
    340		data->auto_corr_ofdm_mrc_x1 =
    341			min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val);
    342	}
    343
    344	/* Else if we got fewer than desired, increase sensitivity */
    345	else if (false_alarms < min_false_alarms) {
    346
    347		IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n",
    348			     false_alarms, min_false_alarms);
    349
    350		val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
    351		data->auto_corr_ofdm =
    352			max((u32)ranges->auto_corr_min_ofdm, val);
    353
    354		val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
    355		data->auto_corr_ofdm_mrc =
    356			max((u32)ranges->auto_corr_min_ofdm_mrc, val);
    357
    358		val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
    359		data->auto_corr_ofdm_x1 =
    360			max((u32)ranges->auto_corr_min_ofdm_x1, val);
    361
    362		val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
    363		data->auto_corr_ofdm_mrc_x1 =
    364			max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val);
    365	} else {
    366		IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n",
    367			 min_false_alarms, false_alarms, max_false_alarms);
    368	}
    369	return 0;
    370}
    371
    372static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv,
    373				struct iwl_sensitivity_data *data,
    374				__le16 *tbl)
    375{
    376	tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
    377				cpu_to_le16((u16)data->auto_corr_ofdm);
    378	tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
    379				cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
    380	tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
    381				cpu_to_le16((u16)data->auto_corr_ofdm_x1);
    382	tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
    383				cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
    384
    385	tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
    386				cpu_to_le16((u16)data->auto_corr_cck);
    387	tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
    388				cpu_to_le16((u16)data->auto_corr_cck_mrc);
    389
    390	tbl[HD_MIN_ENERGY_CCK_DET_INDEX] =
    391				cpu_to_le16((u16)data->nrg_th_cck);
    392	tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] =
    393				cpu_to_le16((u16)data->nrg_th_ofdm);
    394
    395	tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
    396				cpu_to_le16(data->barker_corr_th_min);
    397	tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
    398				cpu_to_le16(data->barker_corr_th_min_mrc);
    399	tbl[HD_OFDM_ENERGY_TH_IN_INDEX] =
    400				cpu_to_le16(data->nrg_th_cca);
    401
    402	IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
    403			data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
    404			data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
    405			data->nrg_th_ofdm);
    406
    407	IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n",
    408			data->auto_corr_cck, data->auto_corr_cck_mrc,
    409			data->nrg_th_cck);
    410}
    411
    412/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
    413static int iwl_sensitivity_write(struct iwl_priv *priv)
    414{
    415	struct iwl_sensitivity_cmd cmd;
    416	struct iwl_sensitivity_data *data = NULL;
    417	struct iwl_host_cmd cmd_out = {
    418		.id = SENSITIVITY_CMD,
    419		.len = { sizeof(struct iwl_sensitivity_cmd), },
    420		.flags = CMD_ASYNC,
    421		.data = { &cmd, },
    422	};
    423
    424	data = &(priv->sensitivity_data);
    425
    426	memset(&cmd, 0, sizeof(cmd));
    427
    428	iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]);
    429
    430	/* Update uCode's "work" table, and copy it to DSP */
    431	cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
    432
    433	/* Don't send command to uCode if nothing has changed */
    434	if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
    435		    sizeof(u16)*HD_TABLE_SIZE)) {
    436		IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
    437		return 0;
    438	}
    439
    440	/* Copy table for comparison next time */
    441	memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
    442	       sizeof(u16)*HD_TABLE_SIZE);
    443
    444	return iwl_dvm_send_cmd(priv, &cmd_out);
    445}
    446
    447/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
    448static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
    449{
    450	struct iwl_enhance_sensitivity_cmd cmd;
    451	struct iwl_sensitivity_data *data = NULL;
    452	struct iwl_host_cmd cmd_out = {
    453		.id = SENSITIVITY_CMD,
    454		.len = { sizeof(struct iwl_enhance_sensitivity_cmd), },
    455		.flags = CMD_ASYNC,
    456		.data = { &cmd, },
    457	};
    458
    459	data = &(priv->sensitivity_data);
    460
    461	memset(&cmd, 0, sizeof(cmd));
    462
    463	iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
    464
    465	if (priv->lib->hd_v2) {
    466		cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
    467			HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
    468		cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
    469			HD_INA_NON_SQUARE_DET_CCK_DATA_V2;
    470		cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
    471			HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2;
    472		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
    473			HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
    474		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
    475			HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
    476		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
    477			HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2;
    478		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
    479			HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2;
    480		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
    481			HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
    482		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
    483			HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
    484		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
    485			HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2;
    486		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
    487			HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2;
    488	} else {
    489		cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
    490			HD_INA_NON_SQUARE_DET_OFDM_DATA_V1;
    491		cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
    492			HD_INA_NON_SQUARE_DET_CCK_DATA_V1;
    493		cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
    494			HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1;
    495		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
    496			HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
    497		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
    498			HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
    499		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
    500			HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1;
    501		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
    502			HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1;
    503		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
    504			HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
    505		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
    506			HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
    507		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
    508			HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1;
    509		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
    510			HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1;
    511	}
    512
    513	/* Update uCode's "work" table, and copy it to DSP */
    514	cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
    515
    516	/* Don't send command to uCode if nothing has changed */
    517	if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]),
    518		    sizeof(u16)*HD_TABLE_SIZE) &&
    519	    !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX],
    520		    &(priv->enhance_sensitivity_tbl[0]),
    521		    sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) {
    522		IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
    523		return 0;
    524	}
    525
    526	/* Copy table for comparison next time */
    527	memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]),
    528	       sizeof(u16)*HD_TABLE_SIZE);
    529	memcpy(&(priv->enhance_sensitivity_tbl[0]),
    530	       &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]),
    531	       sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES);
    532
    533	return iwl_dvm_send_cmd(priv, &cmd_out);
    534}
    535
    536void iwl_init_sensitivity(struct iwl_priv *priv)
    537{
    538	int ret = 0;
    539	int i;
    540	struct iwl_sensitivity_data *data = NULL;
    541	const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
    542
    543	if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
    544		return;
    545
    546	IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n");
    547
    548	/* Clear driver's sensitivity algo data */
    549	data = &(priv->sensitivity_data);
    550
    551	if (ranges == NULL)
    552		return;
    553
    554	memset(data, 0, sizeof(struct iwl_sensitivity_data));
    555
    556	data->num_in_cck_no_fa = 0;
    557	data->nrg_curr_state = IWL_FA_TOO_MANY;
    558	data->nrg_prev_state = IWL_FA_TOO_MANY;
    559	data->nrg_silence_ref = 0;
    560	data->nrg_silence_idx = 0;
    561	data->nrg_energy_idx = 0;
    562
    563	for (i = 0; i < 10; i++)
    564		data->nrg_value[i] = 0;
    565
    566	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
    567		data->nrg_silence_rssi[i] = 0;
    568
    569	data->auto_corr_ofdm =  ranges->auto_corr_min_ofdm;
    570	data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
    571	data->auto_corr_ofdm_x1  = ranges->auto_corr_min_ofdm_x1;
    572	data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
    573	data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
    574	data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
    575	data->nrg_th_cck = ranges->nrg_th_cck;
    576	data->nrg_th_ofdm = ranges->nrg_th_ofdm;
    577	data->barker_corr_th_min = ranges->barker_corr_th_min;
    578	data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
    579	data->nrg_th_cca = ranges->nrg_th_cca;
    580
    581	data->last_bad_plcp_cnt_ofdm = 0;
    582	data->last_fa_cnt_ofdm = 0;
    583	data->last_bad_plcp_cnt_cck = 0;
    584	data->last_fa_cnt_cck = 0;
    585
    586	if (priv->fw->enhance_sensitivity_table)
    587		ret |= iwl_enhance_sensitivity_write(priv);
    588	else
    589		ret |= iwl_sensitivity_write(priv);
    590	IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret);
    591}
    592
    593void iwl_sensitivity_calibration(struct iwl_priv *priv)
    594{
    595	u32 rx_enable_time;
    596	u32 fa_cck;
    597	u32 fa_ofdm;
    598	u32 bad_plcp_cck;
    599	u32 bad_plcp_ofdm;
    600	u32 norm_fa_ofdm;
    601	u32 norm_fa_cck;
    602	struct iwl_sensitivity_data *data = NULL;
    603	struct statistics_rx_non_phy *rx_info;
    604	struct statistics_rx_phy *ofdm, *cck;
    605	struct statistics_general_data statis;
    606
    607	if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
    608		return;
    609
    610	data = &(priv->sensitivity_data);
    611
    612	if (!iwl_is_any_associated(priv)) {
    613		IWL_DEBUG_CALIB(priv, "<< - not associated\n");
    614		return;
    615	}
    616
    617	spin_lock_bh(&priv->statistics.lock);
    618	rx_info = &priv->statistics.rx_non_phy;
    619	ofdm = &priv->statistics.rx_ofdm;
    620	cck = &priv->statistics.rx_cck;
    621	if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
    622		IWL_DEBUG_CALIB(priv, "<< invalid data.\n");
    623		spin_unlock_bh(&priv->statistics.lock);
    624		return;
    625	}
    626
    627	/* Extract Statistics: */
    628	rx_enable_time = le32_to_cpu(rx_info->channel_load);
    629	fa_cck = le32_to_cpu(cck->false_alarm_cnt);
    630	fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
    631	bad_plcp_cck = le32_to_cpu(cck->plcp_err);
    632	bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);
    633
    634	statis.beacon_silence_rssi_a =
    635			le32_to_cpu(rx_info->beacon_silence_rssi_a);
    636	statis.beacon_silence_rssi_b =
    637			le32_to_cpu(rx_info->beacon_silence_rssi_b);
    638	statis.beacon_silence_rssi_c =
    639			le32_to_cpu(rx_info->beacon_silence_rssi_c);
    640	statis.beacon_energy_a =
    641			le32_to_cpu(rx_info->beacon_energy_a);
    642	statis.beacon_energy_b =
    643			le32_to_cpu(rx_info->beacon_energy_b);
    644	statis.beacon_energy_c =
    645			le32_to_cpu(rx_info->beacon_energy_c);
    646
    647	spin_unlock_bh(&priv->statistics.lock);
    648
    649	IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time);
    650
    651	if (!rx_enable_time) {
    652		IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n");
    653		return;
    654	}
    655
    656	/* These statistics increase monotonically, and do not reset
    657	 *   at each beacon.  Calculate difference from last value, or just
    658	 *   use the new statistics value if it has reset or wrapped around. */
    659	if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
    660		data->last_bad_plcp_cnt_cck = bad_plcp_cck;
    661	else {
    662		bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
    663		data->last_bad_plcp_cnt_cck += bad_plcp_cck;
    664	}
    665
    666	if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
    667		data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
    668	else {
    669		bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
    670		data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
    671	}
    672
    673	if (data->last_fa_cnt_ofdm > fa_ofdm)
    674		data->last_fa_cnt_ofdm = fa_ofdm;
    675	else {
    676		fa_ofdm -= data->last_fa_cnt_ofdm;
    677		data->last_fa_cnt_ofdm += fa_ofdm;
    678	}
    679
    680	if (data->last_fa_cnt_cck > fa_cck)
    681		data->last_fa_cnt_cck = fa_cck;
    682	else {
    683		fa_cck -= data->last_fa_cnt_cck;
    684		data->last_fa_cnt_cck += fa_cck;
    685	}
    686
    687	/* Total aborted signal locks */
    688	norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
    689	norm_fa_cck = fa_cck + bad_plcp_cck;
    690
    691	IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u  ofdm: fa %u badp %u\n", fa_cck,
    692			bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
    693
    694	iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
    695	iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
    696	if (priv->fw->enhance_sensitivity_table)
    697		iwl_enhance_sensitivity_write(priv);
    698	else
    699		iwl_sensitivity_write(priv);
    700}
    701
    702static inline u8 find_first_chain(u8 mask)
    703{
    704	if (mask & ANT_A)
    705		return CHAIN_A;
    706	if (mask & ANT_B)
    707		return CHAIN_B;
    708	return CHAIN_C;
    709}
    710
    711/*
    712 * Run disconnected antenna algorithm to find out which antennas are
    713 * disconnected.
    714 */
    715static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
    716				     struct iwl_chain_noise_data *data)
    717{
    718	u32 active_chains = 0;
    719	u32 max_average_sig;
    720	u16 max_average_sig_antenna_i;
    721	u8 num_tx_chains;
    722	u8 first_chain;
    723	u16 i = 0;
    724
    725	average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS;
    726	average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS;
    727	average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS;
    728
    729	if (average_sig[0] >= average_sig[1]) {
    730		max_average_sig = average_sig[0];
    731		max_average_sig_antenna_i = 0;
    732		active_chains = (1 << max_average_sig_antenna_i);
    733	} else {
    734		max_average_sig = average_sig[1];
    735		max_average_sig_antenna_i = 1;
    736		active_chains = (1 << max_average_sig_antenna_i);
    737	}
    738
    739	if (average_sig[2] >= max_average_sig) {
    740		max_average_sig = average_sig[2];
    741		max_average_sig_antenna_i = 2;
    742		active_chains = (1 << max_average_sig_antenna_i);
    743	}
    744
    745	IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n",
    746		     average_sig[0], average_sig[1], average_sig[2]);
    747	IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n",
    748		     max_average_sig, max_average_sig_antenna_i);
    749
    750	/* Compare signal strengths for all 3 receivers. */
    751	for (i = 0; i < NUM_RX_CHAINS; i++) {
    752		if (i != max_average_sig_antenna_i) {
    753			s32 rssi_delta = (max_average_sig - average_sig[i]);
    754
    755			/* If signal is very weak, compared with
    756			 * strongest, mark it as disconnected. */
    757			if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
    758				data->disconn_array[i] = 1;
    759			else
    760				active_chains |= (1 << i);
    761			IWL_DEBUG_CALIB(priv, "i = %d  rssiDelta = %d  "
    762			     "disconn_array[i] = %d\n",
    763			     i, rssi_delta, data->disconn_array[i]);
    764		}
    765	}
    766
    767	/*
    768	 * The above algorithm sometimes fails when the ucode
    769	 * reports 0 for all chains. It's not clear why that
    770	 * happens to start with, but it is then causing trouble
    771	 * because this can make us enable more chains than the
    772	 * hardware really has.
    773	 *
    774	 * To be safe, simply mask out any chains that we know
    775	 * are not on the device.
    776	 */
    777	active_chains &= priv->nvm_data->valid_rx_ant;
    778
    779	num_tx_chains = 0;
    780	for (i = 0; i < NUM_RX_CHAINS; i++) {
    781		/* loops on all the bits of
    782		 * priv->hw_setting.valid_tx_ant */
    783		u8 ant_msk = (1 << i);
    784		if (!(priv->nvm_data->valid_tx_ant & ant_msk))
    785			continue;
    786
    787		num_tx_chains++;
    788		if (data->disconn_array[i] == 0)
    789			/* there is a Tx antenna connected */
    790			break;
    791		if (num_tx_chains == priv->hw_params.tx_chains_num &&
    792		    data->disconn_array[i]) {
    793			/*
    794			 * If all chains are disconnected
    795			 * connect the first valid tx chain
    796			 */
    797			first_chain =
    798				find_first_chain(priv->nvm_data->valid_tx_ant);
    799			data->disconn_array[first_chain] = 0;
    800			active_chains |= BIT(first_chain);
    801			IWL_DEBUG_CALIB(priv,
    802					"All Tx chains are disconnected W/A - declare %d as connected\n",
    803					first_chain);
    804			break;
    805		}
    806	}
    807
    808	if (active_chains != priv->nvm_data->valid_rx_ant &&
    809	    active_chains != priv->chain_noise_data.active_chains)
    810		IWL_DEBUG_CALIB(priv,
    811				"Detected that not all antennas are connected! "
    812				"Connected: %#x, valid: %#x.\n",
    813				active_chains,
    814				priv->nvm_data->valid_rx_ant);
    815
    816	/* Save for use within RXON, TX, SCAN commands, etc. */
    817	data->active_chains = active_chains;
    818	IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n",
    819			active_chains);
    820}
    821
    822static void iwlagn_gain_computation(struct iwl_priv *priv,
    823				    u32 average_noise[NUM_RX_CHAINS],
    824				    u8 default_chain)
    825{
    826	int i;
    827	s32 delta_g;
    828	struct iwl_chain_noise_data *data = &priv->chain_noise_data;
    829
    830	/*
    831	 * Find Gain Code for the chains based on "default chain"
    832	 */
    833	for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
    834		if ((data->disconn_array[i])) {
    835			data->delta_gain_code[i] = 0;
    836			continue;
    837		}
    838
    839		delta_g = (priv->lib->chain_noise_scale *
    840			((s32)average_noise[default_chain] -
    841			(s32)average_noise[i])) / 1500;
    842
    843		/* bound gain by 2 bits value max, 3rd bit is sign */
    844		data->delta_gain_code[i] =
    845			min(abs(delta_g), CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
    846
    847		if (delta_g < 0)
    848			/*
    849			 * set negative sign ...
    850			 * note to Intel developers:  This is uCode API format,
    851			 *   not the format of any internal device registers.
    852			 *   Do not change this format for e.g. 6050 or similar
    853			 *   devices.  Change format only if more resolution
    854			 *   (i.e. more than 2 bits magnitude) is needed.
    855			 */
    856			data->delta_gain_code[i] |= (1 << 2);
    857	}
    858
    859	IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d  ANT_C = %d\n",
    860			data->delta_gain_code[1], data->delta_gain_code[2]);
    861
    862	if (!data->radio_write) {
    863		struct iwl_calib_chain_noise_gain_cmd cmd;
    864
    865		memset(&cmd, 0, sizeof(cmd));
    866
    867		iwl_set_calib_hdr(&cmd.hdr,
    868			priv->phy_calib_chain_noise_gain_cmd);
    869		cmd.delta_gain_1 = data->delta_gain_code[1];
    870		cmd.delta_gain_2 = data->delta_gain_code[2];
    871		iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
    872			CMD_ASYNC, sizeof(cmd), &cmd);
    873
    874		data->radio_write = 1;
    875		data->state = IWL_CHAIN_NOISE_CALIBRATED;
    876	}
    877}
    878
    879/*
    880 * Accumulate 16 beacons of signal and noise statistics for each of
    881 *   3 receivers/antennas/rx-chains, then figure out:
    882 * 1)  Which antennas are connected.
    883 * 2)  Differential rx gain settings to balance the 3 receivers.
    884 */
    885void iwl_chain_noise_calibration(struct iwl_priv *priv)
    886{
    887	struct iwl_chain_noise_data *data = NULL;
    888
    889	u32 chain_noise_a;
    890	u32 chain_noise_b;
    891	u32 chain_noise_c;
    892	u32 chain_sig_a;
    893	u32 chain_sig_b;
    894	u32 chain_sig_c;
    895	u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
    896	u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
    897	u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
    898	u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
    899	u16 i = 0;
    900	u16 rxon_chnum = INITIALIZATION_VALUE;
    901	u16 stat_chnum = INITIALIZATION_VALUE;
    902	u8 rxon_band24;
    903	u8 stat_band24;
    904	struct statistics_rx_non_phy *rx_info;
    905
    906	/*
    907	 * MULTI-FIXME:
    908	 * When we support multiple interfaces on different channels,
    909	 * this must be modified/fixed.
    910	 */
    911	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
    912
    913	if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
    914		return;
    915
    916	data = &(priv->chain_noise_data);
    917
    918	/*
    919	 * Accumulate just the first "chain_noise_num_beacons" after
    920	 * the first association, then we're done forever.
    921	 */
    922	if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
    923		if (data->state == IWL_CHAIN_NOISE_ALIVE)
    924			IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n");
    925		return;
    926	}
    927
    928	spin_lock_bh(&priv->statistics.lock);
    929
    930	rx_info = &priv->statistics.rx_non_phy;
    931
    932	if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
    933		IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n");
    934		spin_unlock_bh(&priv->statistics.lock);
    935		return;
    936	}
    937
    938	rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK);
    939	rxon_chnum = le16_to_cpu(ctx->staging.channel);
    940	stat_band24 =
    941		!!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK);
    942	stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16;
    943
    944	/* Make sure we accumulate data for just the associated channel
    945	 *   (even if scanning). */
    946	if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) {
    947		IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n",
    948				rxon_chnum, rxon_band24);
    949		spin_unlock_bh(&priv->statistics.lock);
    950		return;
    951	}
    952
    953	/*
    954	 *  Accumulate beacon statistics values across
    955	 * "chain_noise_num_beacons"
    956	 */
    957	chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
    958				IN_BAND_FILTER;
    959	chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
    960				IN_BAND_FILTER;
    961	chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
    962				IN_BAND_FILTER;
    963
    964	chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
    965	chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
    966	chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
    967
    968	spin_unlock_bh(&priv->statistics.lock);
    969
    970	data->beacon_count++;
    971
    972	data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
    973	data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
    974	data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
    975
    976	data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
    977	data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
    978	data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
    979
    980	IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n",
    981			rxon_chnum, rxon_band24, data->beacon_count);
    982	IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n",
    983			chain_sig_a, chain_sig_b, chain_sig_c);
    984	IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n",
    985			chain_noise_a, chain_noise_b, chain_noise_c);
    986
    987	/* If this is the "chain_noise_num_beacons", determine:
    988	 * 1)  Disconnected antennas (using signal strengths)
    989	 * 2)  Differential gain (using silence noise) to balance receivers */
    990	if (data->beacon_count != IWL_CAL_NUM_BEACONS)
    991		return;
    992
    993	/* Analyze signal for disconnected antenna */
    994	if (priv->lib->bt_params &&
    995	    priv->lib->bt_params->advanced_bt_coexist) {
    996		/* Disable disconnected antenna algorithm for advanced
    997		   bt coex, assuming valid antennas are connected */
    998		data->active_chains = priv->nvm_data->valid_rx_ant;
    999		for (i = 0; i < NUM_RX_CHAINS; i++)
   1000			if (!(data->active_chains & (1<<i)))
   1001				data->disconn_array[i] = 1;
   1002	} else
   1003		iwl_find_disconn_antenna(priv, average_sig, data);
   1004
   1005	/* Analyze noise for rx balance */
   1006	average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS;
   1007	average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS;
   1008	average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS;
   1009
   1010	for (i = 0; i < NUM_RX_CHAINS; i++) {
   1011		if (!(data->disconn_array[i]) &&
   1012		   (average_noise[i] <= min_average_noise)) {
   1013			/* This means that chain i is active and has
   1014			 * lower noise values so far: */
   1015			min_average_noise = average_noise[i];
   1016			min_average_noise_antenna_i = i;
   1017		}
   1018	}
   1019
   1020	IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n",
   1021			average_noise[0], average_noise[1],
   1022			average_noise[2]);
   1023
   1024	IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n",
   1025			min_average_noise, min_average_noise_antenna_i);
   1026
   1027	iwlagn_gain_computation(
   1028		priv, average_noise,
   1029		find_first_chain(priv->nvm_data->valid_rx_ant));
   1030
   1031	/* Some power changes may have been made during the calibration.
   1032	 * Update and commit the RXON
   1033	 */
   1034	iwl_update_chain_flags(priv);
   1035
   1036	data->state = IWL_CHAIN_NOISE_DONE;
   1037	iwl_power_update_mode(priv, false);
   1038}
   1039
   1040void iwl_reset_run_time_calib(struct iwl_priv *priv)
   1041{
   1042	int i;
   1043	memset(&(priv->sensitivity_data), 0,
   1044	       sizeof(struct iwl_sensitivity_data));
   1045	memset(&(priv->chain_noise_data), 0,
   1046	       sizeof(struct iwl_chain_noise_data));
   1047	for (i = 0; i < NUM_RX_CHAINS; i++)
   1048		priv->chain_noise_data.delta_gain_code[i] =
   1049				CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
   1050
   1051	/* Ask for statistics now, the uCode will send notification
   1052	 * periodically after association */
   1053	iwl_send_statistics_request(priv, CMD_ASYNC, true);
   1054}