Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * Copyright (C) 2005-2014 Intel Corporation
0004  */
0005 #include <linux/slab.h>
0006 #include <net/mac80211.h>
0007 
0008 #include "iwl-trans.h"
0009 
0010 #include "dev.h"
0011 #include "calib.h"
0012 #include "agn.h"
0013 
0014 /*****************************************************************************
0015  * INIT calibrations framework
0016  *****************************************************************************/
0017 
0018 /* Opaque calibration results */
0019 struct iwl_calib_result {
0020     struct list_head list;
0021     size_t cmd_len;
0022     struct iwl_calib_hdr hdr;
0023     /* data follows */
0024 };
0025 
0026 struct statistics_general_data {
0027     u32 beacon_silence_rssi_a;
0028     u32 beacon_silence_rssi_b;
0029     u32 beacon_silence_rssi_c;
0030     u32 beacon_energy_a;
0031     u32 beacon_energy_b;
0032     u32 beacon_energy_c;
0033 };
0034 
0035 int iwl_send_calib_results(struct iwl_priv *priv)
0036 {
0037     struct iwl_host_cmd hcmd = {
0038         .id = REPLY_PHY_CALIBRATION_CMD,
0039     };
0040     struct iwl_calib_result *res;
0041 
0042     list_for_each_entry(res, &priv->calib_results, list) {
0043         int ret;
0044 
0045         hcmd.len[0] = res->cmd_len;
0046         hcmd.data[0] = &res->hdr;
0047         hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
0048         ret = iwl_dvm_send_cmd(priv, &hcmd);
0049         if (ret) {
0050             IWL_ERR(priv, "Error %d on calib cmd %d\n",
0051                 ret, res->hdr.op_code);
0052             return ret;
0053         }
0054     }
0055 
0056     return 0;
0057 }
0058 
0059 int iwl_calib_set(struct iwl_priv *priv,
0060           const struct iwl_calib_hdr *cmd, int len)
0061 {
0062     struct iwl_calib_result *res, *tmp;
0063 
0064     res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr),
0065               GFP_ATOMIC);
0066     if (!res)
0067         return -ENOMEM;
0068     memcpy(&res->hdr, cmd, len);
0069     res->cmd_len = len;
0070 
0071     list_for_each_entry(tmp, &priv->calib_results, list) {
0072         if (tmp->hdr.op_code == res->hdr.op_code) {
0073             list_replace(&tmp->list, &res->list);
0074             kfree(tmp);
0075             return 0;
0076         }
0077     }
0078 
0079     /* wasn't in list already */
0080     list_add_tail(&res->list, &priv->calib_results);
0081 
0082     return 0;
0083 }
0084 
0085 void iwl_calib_free_results(struct iwl_priv *priv)
0086 {
0087     struct iwl_calib_result *res, *tmp;
0088 
0089     list_for_each_entry_safe(res, tmp, &priv->calib_results, list) {
0090         list_del(&res->list);
0091         kfree(res);
0092     }
0093 }
0094 
0095 /*****************************************************************************
0096  * RUNTIME calibrations framework
0097  *****************************************************************************/
0098 
0099 /* "false alarms" are signals that our DSP tries to lock onto,
0100  *   but then determines that they are either noise, or transmissions
0101  *   from a distant wireless network (also "noise", really) that get
0102  *   "stepped on" by stronger transmissions within our own network.
0103  * This algorithm attempts to set a sensitivity level that is high
0104  *   enough to receive all of our own network traffic, but not so
0105  *   high that our DSP gets too busy trying to lock onto non-network
0106  *   activity/noise. */
0107 static int iwl_sens_energy_cck(struct iwl_priv *priv,
0108                    u32 norm_fa,
0109                    u32 rx_enable_time,
0110                    struct statistics_general_data *rx_info)
0111 {
0112     u32 max_nrg_cck = 0;
0113     int i = 0;
0114     u8 max_silence_rssi = 0;
0115     u32 silence_ref = 0;
0116     u8 silence_rssi_a = 0;
0117     u8 silence_rssi_b = 0;
0118     u8 silence_rssi_c = 0;
0119     u32 val;
0120 
0121     /* "false_alarms" values below are cross-multiplications to assess the
0122      *   numbers of false alarms within the measured period of actual Rx
0123      *   (Rx is off when we're txing), vs the min/max expected false alarms
0124      *   (some should be expected if rx is sensitive enough) in a
0125      *   hypothetical listening period of 200 time units (TU), 204.8 msec:
0126      *
0127      * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
0128      *
0129      * */
0130     u32 false_alarms = norm_fa * 200 * 1024;
0131     u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
0132     u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
0133     struct iwl_sensitivity_data *data = NULL;
0134     const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
0135 
0136     data = &(priv->sensitivity_data);
0137 
0138     data->nrg_auto_corr_silence_diff = 0;
0139 
0140     /* Find max silence rssi among all 3 receivers.
0141      * This is background noise, which may include transmissions from other
0142      *    networks, measured during silence before our network's beacon */
0143     silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
0144                 ALL_BAND_FILTER) >> 8);
0145     silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
0146                 ALL_BAND_FILTER) >> 8);
0147     silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
0148                 ALL_BAND_FILTER) >> 8);
0149 
0150     val = max(silence_rssi_b, silence_rssi_c);
0151     max_silence_rssi = max(silence_rssi_a, (u8) val);
0152 
0153     /* Store silence rssi in 20-beacon history table */
0154     data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
0155     data->nrg_silence_idx++;
0156     if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
0157         data->nrg_silence_idx = 0;
0158 
0159     /* Find max silence rssi across 20 beacon history */
0160     for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
0161         val = data->nrg_silence_rssi[i];
0162         silence_ref = max(silence_ref, val);
0163     }
0164     IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n",
0165             silence_rssi_a, silence_rssi_b, silence_rssi_c,
0166             silence_ref);
0167 
0168     /* Find max rx energy (min value!) among all 3 receivers,
0169      *   measured during beacon frame.
0170      * Save it in 10-beacon history table. */
0171     i = data->nrg_energy_idx;
0172     val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
0173     data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
0174 
0175     data->nrg_energy_idx++;
0176     if (data->nrg_energy_idx >= 10)
0177         data->nrg_energy_idx = 0;
0178 
0179     /* Find min rx energy (max value) across 10 beacon history.
0180      * This is the minimum signal level that we want to receive well.
0181      * Add backoff (margin so we don't miss slightly lower energy frames).
0182      * This establishes an upper bound (min value) for energy threshold. */
0183     max_nrg_cck = data->nrg_value[0];
0184     for (i = 1; i < 10; i++)
0185         max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
0186     max_nrg_cck += 6;
0187 
0188     IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
0189             rx_info->beacon_energy_a, rx_info->beacon_energy_b,
0190             rx_info->beacon_energy_c, max_nrg_cck - 6);
0191 
0192     /* Count number of consecutive beacons with fewer-than-desired
0193      *   false alarms. */
0194     if (false_alarms < min_false_alarms)
0195         data->num_in_cck_no_fa++;
0196     else
0197         data->num_in_cck_no_fa = 0;
0198     IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n",
0199             data->num_in_cck_no_fa);
0200 
0201     /* If we got too many false alarms this time, reduce sensitivity */
0202     if ((false_alarms > max_false_alarms) &&
0203         (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) {
0204         IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n",
0205              false_alarms, max_false_alarms);
0206         IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n");
0207         data->nrg_curr_state = IWL_FA_TOO_MANY;
0208         /* Store for "fewer than desired" on later beacon */
0209         data->nrg_silence_ref = silence_ref;
0210 
0211         /* increase energy threshold (reduce nrg value)
0212          *   to decrease sensitivity */
0213         data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK;
0214     /* Else if we got fewer than desired, increase sensitivity */
0215     } else if (false_alarms < min_false_alarms) {
0216         data->nrg_curr_state = IWL_FA_TOO_FEW;
0217 
0218         /* Compare silence level with silence level for most recent
0219          *   healthy number or too many false alarms */
0220         data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
0221                            (s32)silence_ref;
0222 
0223         IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n",
0224              false_alarms, min_false_alarms,
0225              data->nrg_auto_corr_silence_diff);
0226 
0227         /* Increase value to increase sensitivity, but only if:
0228          * 1a) previous beacon did *not* have *too many* false alarms
0229          * 1b) AND there's a significant difference in Rx levels
0230          *      from a previous beacon with too many, or healthy # FAs
0231          * OR 2) We've seen a lot of beacons (100) with too few
0232          *       false alarms */
0233         if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
0234             ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
0235             (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
0236 
0237             IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n");
0238             /* Increase nrg value to increase sensitivity */
0239             val = data->nrg_th_cck + NRG_STEP_CCK;
0240             data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val);
0241         } else {
0242             IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n");
0243         }
0244 
0245     /* Else we got a healthy number of false alarms, keep status quo */
0246     } else {
0247         IWL_DEBUG_CALIB(priv, " FA in safe zone\n");
0248         data->nrg_curr_state = IWL_FA_GOOD_RANGE;
0249 
0250         /* Store for use in "fewer than desired" with later beacon */
0251         data->nrg_silence_ref = silence_ref;
0252 
0253         /* If previous beacon had too many false alarms,
0254          *   give it some extra margin by reducing sensitivity again
0255          *   (but don't go below measured energy of desired Rx) */
0256         if (data->nrg_prev_state == IWL_FA_TOO_MANY) {
0257             IWL_DEBUG_CALIB(priv, "... increasing margin\n");
0258             if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
0259                 data->nrg_th_cck -= NRG_MARGIN;
0260             else
0261                 data->nrg_th_cck = max_nrg_cck;
0262         }
0263     }
0264 
0265     /* Make sure the energy threshold does not go above the measured
0266      * energy of the desired Rx signals (reduced by backoff margin),
0267      * or else we might start missing Rx frames.
0268      * Lower value is higher energy, so we use max()!
0269      */
0270     data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
0271     IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck);
0272 
0273     data->nrg_prev_state = data->nrg_curr_state;
0274 
0275     /* Auto-correlation CCK algorithm */
0276     if (false_alarms > min_false_alarms) {
0277 
0278         /* increase auto_corr values to decrease sensitivity
0279          * so the DSP won't be disturbed by the noise
0280          */
0281         if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
0282             data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
0283         else {
0284             val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
0285             data->auto_corr_cck =
0286                 min((u32)ranges->auto_corr_max_cck, val);
0287         }
0288         val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
0289         data->auto_corr_cck_mrc =
0290             min((u32)ranges->auto_corr_max_cck_mrc, val);
0291     } else if ((false_alarms < min_false_alarms) &&
0292        ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
0293        (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
0294 
0295         /* Decrease auto_corr values to increase sensitivity */
0296         val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
0297         data->auto_corr_cck =
0298             max((u32)ranges->auto_corr_min_cck, val);
0299         val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
0300         data->auto_corr_cck_mrc =
0301             max((u32)ranges->auto_corr_min_cck_mrc, val);
0302     }
0303 
0304     return 0;
0305 }
0306 
0307 
0308 static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
0309                        u32 norm_fa,
0310                        u32 rx_enable_time)
0311 {
0312     u32 val;
0313     u32 false_alarms = norm_fa * 200 * 1024;
0314     u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
0315     u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
0316     struct iwl_sensitivity_data *data = NULL;
0317     const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
0318 
0319     data = &(priv->sensitivity_data);
0320 
0321     /* If we got too many false alarms this time, reduce sensitivity */
0322     if (false_alarms > max_false_alarms) {
0323 
0324         IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n",
0325                  false_alarms, max_false_alarms);
0326 
0327         val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
0328         data->auto_corr_ofdm =
0329             min((u32)ranges->auto_corr_max_ofdm, val);
0330 
0331         val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
0332         data->auto_corr_ofdm_mrc =
0333             min((u32)ranges->auto_corr_max_ofdm_mrc, val);
0334 
0335         val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
0336         data->auto_corr_ofdm_x1 =
0337             min((u32)ranges->auto_corr_max_ofdm_x1, val);
0338 
0339         val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
0340         data->auto_corr_ofdm_mrc_x1 =
0341             min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val);
0342     }
0343 
0344     /* Else if we got fewer than desired, increase sensitivity */
0345     else if (false_alarms < min_false_alarms) {
0346 
0347         IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n",
0348                  false_alarms, min_false_alarms);
0349 
0350         val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
0351         data->auto_corr_ofdm =
0352             max((u32)ranges->auto_corr_min_ofdm, val);
0353 
0354         val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
0355         data->auto_corr_ofdm_mrc =
0356             max((u32)ranges->auto_corr_min_ofdm_mrc, val);
0357 
0358         val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
0359         data->auto_corr_ofdm_x1 =
0360             max((u32)ranges->auto_corr_min_ofdm_x1, val);
0361 
0362         val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
0363         data->auto_corr_ofdm_mrc_x1 =
0364             max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val);
0365     } else {
0366         IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n",
0367              min_false_alarms, false_alarms, max_false_alarms);
0368     }
0369     return 0;
0370 }
0371 
0372 static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv,
0373                 struct iwl_sensitivity_data *data,
0374                 __le16 *tbl)
0375 {
0376     tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
0377                 cpu_to_le16((u16)data->auto_corr_ofdm);
0378     tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
0379                 cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
0380     tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
0381                 cpu_to_le16((u16)data->auto_corr_ofdm_x1);
0382     tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
0383                 cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
0384 
0385     tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
0386                 cpu_to_le16((u16)data->auto_corr_cck);
0387     tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
0388                 cpu_to_le16((u16)data->auto_corr_cck_mrc);
0389 
0390     tbl[HD_MIN_ENERGY_CCK_DET_INDEX] =
0391                 cpu_to_le16((u16)data->nrg_th_cck);
0392     tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] =
0393                 cpu_to_le16((u16)data->nrg_th_ofdm);
0394 
0395     tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
0396                 cpu_to_le16(data->barker_corr_th_min);
0397     tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
0398                 cpu_to_le16(data->barker_corr_th_min_mrc);
0399     tbl[HD_OFDM_ENERGY_TH_IN_INDEX] =
0400                 cpu_to_le16(data->nrg_th_cca);
0401 
0402     IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
0403             data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
0404             data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
0405             data->nrg_th_ofdm);
0406 
0407     IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n",
0408             data->auto_corr_cck, data->auto_corr_cck_mrc,
0409             data->nrg_th_cck);
0410 }
0411 
0412 /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
0413 static int iwl_sensitivity_write(struct iwl_priv *priv)
0414 {
0415     struct iwl_sensitivity_cmd cmd;
0416     struct iwl_sensitivity_data *data = NULL;
0417     struct iwl_host_cmd cmd_out = {
0418         .id = SENSITIVITY_CMD,
0419         .len = { sizeof(struct iwl_sensitivity_cmd), },
0420         .flags = CMD_ASYNC,
0421         .data = { &cmd, },
0422     };
0423 
0424     data = &(priv->sensitivity_data);
0425 
0426     memset(&cmd, 0, sizeof(cmd));
0427 
0428     iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]);
0429 
0430     /* Update uCode's "work" table, and copy it to DSP */
0431     cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
0432 
0433     /* Don't send command to uCode if nothing has changed */
0434     if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
0435             sizeof(u16)*HD_TABLE_SIZE)) {
0436         IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
0437         return 0;
0438     }
0439 
0440     /* Copy table for comparison next time */
0441     memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
0442            sizeof(u16)*HD_TABLE_SIZE);
0443 
0444     return iwl_dvm_send_cmd(priv, &cmd_out);
0445 }
0446 
0447 /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
0448 static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
0449 {
0450     struct iwl_enhance_sensitivity_cmd cmd;
0451     struct iwl_sensitivity_data *data = NULL;
0452     struct iwl_host_cmd cmd_out = {
0453         .id = SENSITIVITY_CMD,
0454         .len = { sizeof(struct iwl_enhance_sensitivity_cmd), },
0455         .flags = CMD_ASYNC,
0456         .data = { &cmd, },
0457     };
0458 
0459     data = &(priv->sensitivity_data);
0460 
0461     memset(&cmd, 0, sizeof(cmd));
0462 
0463     iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
0464 
0465     if (priv->lib->hd_v2) {
0466         cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
0467             HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
0468         cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
0469             HD_INA_NON_SQUARE_DET_CCK_DATA_V2;
0470         cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
0471             HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2;
0472         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
0473             HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
0474         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
0475             HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
0476         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
0477             HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2;
0478         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
0479             HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2;
0480         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
0481             HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
0482         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
0483             HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
0484         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
0485             HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2;
0486         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
0487             HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2;
0488     } else {
0489         cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
0490             HD_INA_NON_SQUARE_DET_OFDM_DATA_V1;
0491         cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
0492             HD_INA_NON_SQUARE_DET_CCK_DATA_V1;
0493         cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
0494             HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1;
0495         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
0496             HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
0497         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
0498             HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
0499         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
0500             HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1;
0501         cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
0502             HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1;
0503         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
0504             HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
0505         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
0506             HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
0507         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
0508             HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1;
0509         cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
0510             HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1;
0511     }
0512 
0513     /* Update uCode's "work" table, and copy it to DSP */
0514     cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
0515 
0516     /* Don't send command to uCode if nothing has changed */
0517     if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]),
0518             sizeof(u16)*HD_TABLE_SIZE) &&
0519         !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX],
0520             &(priv->enhance_sensitivity_tbl[0]),
0521             sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) {
0522         IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
0523         return 0;
0524     }
0525 
0526     /* Copy table for comparison next time */
0527     memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]),
0528            sizeof(u16)*HD_TABLE_SIZE);
0529     memcpy(&(priv->enhance_sensitivity_tbl[0]),
0530            &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]),
0531            sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES);
0532 
0533     return iwl_dvm_send_cmd(priv, &cmd_out);
0534 }
0535 
0536 void iwl_init_sensitivity(struct iwl_priv *priv)
0537 {
0538     int ret = 0;
0539     int i;
0540     struct iwl_sensitivity_data *data = NULL;
0541     const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
0542 
0543     if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
0544         return;
0545 
0546     IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n");
0547 
0548     /* Clear driver's sensitivity algo data */
0549     data = &(priv->sensitivity_data);
0550 
0551     if (ranges == NULL)
0552         return;
0553 
0554     memset(data, 0, sizeof(struct iwl_sensitivity_data));
0555 
0556     data->num_in_cck_no_fa = 0;
0557     data->nrg_curr_state = IWL_FA_TOO_MANY;
0558     data->nrg_prev_state = IWL_FA_TOO_MANY;
0559     data->nrg_silence_ref = 0;
0560     data->nrg_silence_idx = 0;
0561     data->nrg_energy_idx = 0;
0562 
0563     for (i = 0; i < 10; i++)
0564         data->nrg_value[i] = 0;
0565 
0566     for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
0567         data->nrg_silence_rssi[i] = 0;
0568 
0569     data->auto_corr_ofdm =  ranges->auto_corr_min_ofdm;
0570     data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
0571     data->auto_corr_ofdm_x1  = ranges->auto_corr_min_ofdm_x1;
0572     data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
0573     data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
0574     data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
0575     data->nrg_th_cck = ranges->nrg_th_cck;
0576     data->nrg_th_ofdm = ranges->nrg_th_ofdm;
0577     data->barker_corr_th_min = ranges->barker_corr_th_min;
0578     data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
0579     data->nrg_th_cca = ranges->nrg_th_cca;
0580 
0581     data->last_bad_plcp_cnt_ofdm = 0;
0582     data->last_fa_cnt_ofdm = 0;
0583     data->last_bad_plcp_cnt_cck = 0;
0584     data->last_fa_cnt_cck = 0;
0585 
0586     if (priv->fw->enhance_sensitivity_table)
0587         ret |= iwl_enhance_sensitivity_write(priv);
0588     else
0589         ret |= iwl_sensitivity_write(priv);
0590     IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret);
0591 }
0592 
0593 void iwl_sensitivity_calibration(struct iwl_priv *priv)
0594 {
0595     u32 rx_enable_time;
0596     u32 fa_cck;
0597     u32 fa_ofdm;
0598     u32 bad_plcp_cck;
0599     u32 bad_plcp_ofdm;
0600     u32 norm_fa_ofdm;
0601     u32 norm_fa_cck;
0602     struct iwl_sensitivity_data *data = NULL;
0603     struct statistics_rx_non_phy *rx_info;
0604     struct statistics_rx_phy *ofdm, *cck;
0605     struct statistics_general_data statis;
0606 
0607     if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
0608         return;
0609 
0610     data = &(priv->sensitivity_data);
0611 
0612     if (!iwl_is_any_associated(priv)) {
0613         IWL_DEBUG_CALIB(priv, "<< - not associated\n");
0614         return;
0615     }
0616 
0617     spin_lock_bh(&priv->statistics.lock);
0618     rx_info = &priv->statistics.rx_non_phy;
0619     ofdm = &priv->statistics.rx_ofdm;
0620     cck = &priv->statistics.rx_cck;
0621     if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
0622         IWL_DEBUG_CALIB(priv, "<< invalid data.\n");
0623         spin_unlock_bh(&priv->statistics.lock);
0624         return;
0625     }
0626 
0627     /* Extract Statistics: */
0628     rx_enable_time = le32_to_cpu(rx_info->channel_load);
0629     fa_cck = le32_to_cpu(cck->false_alarm_cnt);
0630     fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
0631     bad_plcp_cck = le32_to_cpu(cck->plcp_err);
0632     bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);
0633 
0634     statis.beacon_silence_rssi_a =
0635             le32_to_cpu(rx_info->beacon_silence_rssi_a);
0636     statis.beacon_silence_rssi_b =
0637             le32_to_cpu(rx_info->beacon_silence_rssi_b);
0638     statis.beacon_silence_rssi_c =
0639             le32_to_cpu(rx_info->beacon_silence_rssi_c);
0640     statis.beacon_energy_a =
0641             le32_to_cpu(rx_info->beacon_energy_a);
0642     statis.beacon_energy_b =
0643             le32_to_cpu(rx_info->beacon_energy_b);
0644     statis.beacon_energy_c =
0645             le32_to_cpu(rx_info->beacon_energy_c);
0646 
0647     spin_unlock_bh(&priv->statistics.lock);
0648 
0649     IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time);
0650 
0651     if (!rx_enable_time) {
0652         IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n");
0653         return;
0654     }
0655 
0656     /* These statistics increase monotonically, and do not reset
0657      *   at each beacon.  Calculate difference from last value, or just
0658      *   use the new statistics value if it has reset or wrapped around. */
0659     if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
0660         data->last_bad_plcp_cnt_cck = bad_plcp_cck;
0661     else {
0662         bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
0663         data->last_bad_plcp_cnt_cck += bad_plcp_cck;
0664     }
0665 
0666     if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
0667         data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
0668     else {
0669         bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
0670         data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
0671     }
0672 
0673     if (data->last_fa_cnt_ofdm > fa_ofdm)
0674         data->last_fa_cnt_ofdm = fa_ofdm;
0675     else {
0676         fa_ofdm -= data->last_fa_cnt_ofdm;
0677         data->last_fa_cnt_ofdm += fa_ofdm;
0678     }
0679 
0680     if (data->last_fa_cnt_cck > fa_cck)
0681         data->last_fa_cnt_cck = fa_cck;
0682     else {
0683         fa_cck -= data->last_fa_cnt_cck;
0684         data->last_fa_cnt_cck += fa_cck;
0685     }
0686 
0687     /* Total aborted signal locks */
0688     norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
0689     norm_fa_cck = fa_cck + bad_plcp_cck;
0690 
0691     IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u  ofdm: fa %u badp %u\n", fa_cck,
0692             bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
0693 
0694     iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
0695     iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
0696     if (priv->fw->enhance_sensitivity_table)
0697         iwl_enhance_sensitivity_write(priv);
0698     else
0699         iwl_sensitivity_write(priv);
0700 }
0701 
0702 static inline u8 find_first_chain(u8 mask)
0703 {
0704     if (mask & ANT_A)
0705         return CHAIN_A;
0706     if (mask & ANT_B)
0707         return CHAIN_B;
0708     return CHAIN_C;
0709 }
0710 
0711 /*
0712  * Run disconnected antenna algorithm to find out which antennas are
0713  * disconnected.
0714  */
0715 static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
0716                      struct iwl_chain_noise_data *data)
0717 {
0718     u32 active_chains = 0;
0719     u32 max_average_sig;
0720     u16 max_average_sig_antenna_i;
0721     u8 num_tx_chains;
0722     u8 first_chain;
0723     u16 i = 0;
0724 
0725     average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS;
0726     average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS;
0727     average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS;
0728 
0729     if (average_sig[0] >= average_sig[1]) {
0730         max_average_sig = average_sig[0];
0731         max_average_sig_antenna_i = 0;
0732         active_chains = (1 << max_average_sig_antenna_i);
0733     } else {
0734         max_average_sig = average_sig[1];
0735         max_average_sig_antenna_i = 1;
0736         active_chains = (1 << max_average_sig_antenna_i);
0737     }
0738 
0739     if (average_sig[2] >= max_average_sig) {
0740         max_average_sig = average_sig[2];
0741         max_average_sig_antenna_i = 2;
0742         active_chains = (1 << max_average_sig_antenna_i);
0743     }
0744 
0745     IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n",
0746              average_sig[0], average_sig[1], average_sig[2]);
0747     IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n",
0748              max_average_sig, max_average_sig_antenna_i);
0749 
0750     /* Compare signal strengths for all 3 receivers. */
0751     for (i = 0; i < NUM_RX_CHAINS; i++) {
0752         if (i != max_average_sig_antenna_i) {
0753             s32 rssi_delta = (max_average_sig - average_sig[i]);
0754 
0755             /* If signal is very weak, compared with
0756              * strongest, mark it as disconnected. */
0757             if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
0758                 data->disconn_array[i] = 1;
0759             else
0760                 active_chains |= (1 << i);
0761             IWL_DEBUG_CALIB(priv, "i = %d  rssiDelta = %d  "
0762                  "disconn_array[i] = %d\n",
0763                  i, rssi_delta, data->disconn_array[i]);
0764         }
0765     }
0766 
0767     /*
0768      * The above algorithm sometimes fails when the ucode
0769      * reports 0 for all chains. It's not clear why that
0770      * happens to start with, but it is then causing trouble
0771      * because this can make us enable more chains than the
0772      * hardware really has.
0773      *
0774      * To be safe, simply mask out any chains that we know
0775      * are not on the device.
0776      */
0777     active_chains &= priv->nvm_data->valid_rx_ant;
0778 
0779     num_tx_chains = 0;
0780     for (i = 0; i < NUM_RX_CHAINS; i++) {
0781         /* loops on all the bits of
0782          * priv->hw_setting.valid_tx_ant */
0783         u8 ant_msk = (1 << i);
0784         if (!(priv->nvm_data->valid_tx_ant & ant_msk))
0785             continue;
0786 
0787         num_tx_chains++;
0788         if (data->disconn_array[i] == 0)
0789             /* there is a Tx antenna connected */
0790             break;
0791         if (num_tx_chains == priv->hw_params.tx_chains_num &&
0792             data->disconn_array[i]) {
0793             /*
0794              * If all chains are disconnected
0795              * connect the first valid tx chain
0796              */
0797             first_chain =
0798                 find_first_chain(priv->nvm_data->valid_tx_ant);
0799             data->disconn_array[first_chain] = 0;
0800             active_chains |= BIT(first_chain);
0801             IWL_DEBUG_CALIB(priv,
0802                     "All Tx chains are disconnected W/A - declare %d as connected\n",
0803                     first_chain);
0804             break;
0805         }
0806     }
0807 
0808     if (active_chains != priv->nvm_data->valid_rx_ant &&
0809         active_chains != priv->chain_noise_data.active_chains)
0810         IWL_DEBUG_CALIB(priv,
0811                 "Detected that not all antennas are connected! "
0812                 "Connected: %#x, valid: %#x.\n",
0813                 active_chains,
0814                 priv->nvm_data->valid_rx_ant);
0815 
0816     /* Save for use within RXON, TX, SCAN commands, etc. */
0817     data->active_chains = active_chains;
0818     IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n",
0819             active_chains);
0820 }
0821 
0822 static void iwlagn_gain_computation(struct iwl_priv *priv,
0823                     u32 average_noise[NUM_RX_CHAINS],
0824                     u8 default_chain)
0825 {
0826     int i;
0827     s32 delta_g;
0828     struct iwl_chain_noise_data *data = &priv->chain_noise_data;
0829 
0830     /*
0831      * Find Gain Code for the chains based on "default chain"
0832      */
0833     for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
0834         if ((data->disconn_array[i])) {
0835             data->delta_gain_code[i] = 0;
0836             continue;
0837         }
0838 
0839         delta_g = (priv->lib->chain_noise_scale *
0840             ((s32)average_noise[default_chain] -
0841             (s32)average_noise[i])) / 1500;
0842 
0843         /* bound gain by 2 bits value max, 3rd bit is sign */
0844         data->delta_gain_code[i] =
0845             min(abs(delta_g), CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
0846 
0847         if (delta_g < 0)
0848             /*
0849              * set negative sign ...
0850              * note to Intel developers:  This is uCode API format,
0851              *   not the format of any internal device registers.
0852              *   Do not change this format for e.g. 6050 or similar
0853              *   devices.  Change format only if more resolution
0854              *   (i.e. more than 2 bits magnitude) is needed.
0855              */
0856             data->delta_gain_code[i] |= (1 << 2);
0857     }
0858 
0859     IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d  ANT_C = %d\n",
0860             data->delta_gain_code[1], data->delta_gain_code[2]);
0861 
0862     if (!data->radio_write) {
0863         struct iwl_calib_chain_noise_gain_cmd cmd;
0864 
0865         memset(&cmd, 0, sizeof(cmd));
0866 
0867         iwl_set_calib_hdr(&cmd.hdr,
0868             priv->phy_calib_chain_noise_gain_cmd);
0869         cmd.delta_gain_1 = data->delta_gain_code[1];
0870         cmd.delta_gain_2 = data->delta_gain_code[2];
0871         iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
0872             CMD_ASYNC, sizeof(cmd), &cmd);
0873 
0874         data->radio_write = 1;
0875         data->state = IWL_CHAIN_NOISE_CALIBRATED;
0876     }
0877 }
0878 
0879 /*
0880  * Accumulate 16 beacons of signal and noise statistics for each of
0881  *   3 receivers/antennas/rx-chains, then figure out:
0882  * 1)  Which antennas are connected.
0883  * 2)  Differential rx gain settings to balance the 3 receivers.
0884  */
0885 void iwl_chain_noise_calibration(struct iwl_priv *priv)
0886 {
0887     struct iwl_chain_noise_data *data = NULL;
0888 
0889     u32 chain_noise_a;
0890     u32 chain_noise_b;
0891     u32 chain_noise_c;
0892     u32 chain_sig_a;
0893     u32 chain_sig_b;
0894     u32 chain_sig_c;
0895     u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
0896     u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
0897     u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
0898     u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
0899     u16 i = 0;
0900     u16 rxon_chnum = INITIALIZATION_VALUE;
0901     u16 stat_chnum = INITIALIZATION_VALUE;
0902     u8 rxon_band24;
0903     u8 stat_band24;
0904     struct statistics_rx_non_phy *rx_info;
0905 
0906     /*
0907      * MULTI-FIXME:
0908      * When we support multiple interfaces on different channels,
0909      * this must be modified/fixed.
0910      */
0911     struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
0912 
0913     if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
0914         return;
0915 
0916     data = &(priv->chain_noise_data);
0917 
0918     /*
0919      * Accumulate just the first "chain_noise_num_beacons" after
0920      * the first association, then we're done forever.
0921      */
0922     if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
0923         if (data->state == IWL_CHAIN_NOISE_ALIVE)
0924             IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n");
0925         return;
0926     }
0927 
0928     spin_lock_bh(&priv->statistics.lock);
0929 
0930     rx_info = &priv->statistics.rx_non_phy;
0931 
0932     if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
0933         IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n");
0934         spin_unlock_bh(&priv->statistics.lock);
0935         return;
0936     }
0937 
0938     rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK);
0939     rxon_chnum = le16_to_cpu(ctx->staging.channel);
0940     stat_band24 =
0941         !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK);
0942     stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16;
0943 
0944     /* Make sure we accumulate data for just the associated channel
0945      *   (even if scanning). */
0946     if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) {
0947         IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n",
0948                 rxon_chnum, rxon_band24);
0949         spin_unlock_bh(&priv->statistics.lock);
0950         return;
0951     }
0952 
0953     /*
0954      *  Accumulate beacon statistics values across
0955      * "chain_noise_num_beacons"
0956      */
0957     chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
0958                 IN_BAND_FILTER;
0959     chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
0960                 IN_BAND_FILTER;
0961     chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
0962                 IN_BAND_FILTER;
0963 
0964     chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
0965     chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
0966     chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
0967 
0968     spin_unlock_bh(&priv->statistics.lock);
0969 
0970     data->beacon_count++;
0971 
0972     data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
0973     data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
0974     data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
0975 
0976     data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
0977     data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
0978     data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
0979 
0980     IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n",
0981             rxon_chnum, rxon_band24, data->beacon_count);
0982     IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n",
0983             chain_sig_a, chain_sig_b, chain_sig_c);
0984     IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n",
0985             chain_noise_a, chain_noise_b, chain_noise_c);
0986 
0987     /* If this is the "chain_noise_num_beacons", determine:
0988      * 1)  Disconnected antennas (using signal strengths)
0989      * 2)  Differential gain (using silence noise) to balance receivers */
0990     if (data->beacon_count != IWL_CAL_NUM_BEACONS)
0991         return;
0992 
0993     /* Analyze signal for disconnected antenna */
0994     if (priv->lib->bt_params &&
0995         priv->lib->bt_params->advanced_bt_coexist) {
0996         /* Disable disconnected antenna algorithm for advanced
0997            bt coex, assuming valid antennas are connected */
0998         data->active_chains = priv->nvm_data->valid_rx_ant;
0999         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 
1040 void 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 }