1 /******************************************************************************
5 * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
21 * The full GNU General Public License is included in this distribution
22 * in the file called LICENSE.GPL.
24 * Contact Information:
25 * Intel Linux Wireless <ilw@linux.intel.com>
26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
28 *****************************************************************************/
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/init.h>
33 #include <linux/sched.h>
40 static int iwlagn_send_rxon_assoc(struct iwl_priv
*priv
)
43 struct iwl5000_rxon_assoc_cmd rxon_assoc
;
44 const struct iwl_rxon_cmd
*rxon1
= &priv
->staging_rxon
;
45 const struct iwl_rxon_cmd
*rxon2
= &priv
->active_rxon
;
47 if ((rxon1
->flags
== rxon2
->flags
) &&
48 (rxon1
->filter_flags
== rxon2
->filter_flags
) &&
49 (rxon1
->cck_basic_rates
== rxon2
->cck_basic_rates
) &&
50 (rxon1
->ofdm_ht_single_stream_basic_rates
==
51 rxon2
->ofdm_ht_single_stream_basic_rates
) &&
52 (rxon1
->ofdm_ht_dual_stream_basic_rates
==
53 rxon2
->ofdm_ht_dual_stream_basic_rates
) &&
54 (rxon1
->ofdm_ht_triple_stream_basic_rates
==
55 rxon2
->ofdm_ht_triple_stream_basic_rates
) &&
56 (rxon1
->acquisition_data
== rxon2
->acquisition_data
) &&
57 (rxon1
->rx_chain
== rxon2
->rx_chain
) &&
58 (rxon1
->ofdm_basic_rates
== rxon2
->ofdm_basic_rates
)) {
59 IWL_DEBUG_INFO(priv
, "Using current RXON_ASSOC. Not resending.\n");
63 rxon_assoc
.flags
= priv
->staging_rxon
.flags
;
64 rxon_assoc
.filter_flags
= priv
->staging_rxon
.filter_flags
;
65 rxon_assoc
.ofdm_basic_rates
= priv
->staging_rxon
.ofdm_basic_rates
;
66 rxon_assoc
.cck_basic_rates
= priv
->staging_rxon
.cck_basic_rates
;
67 rxon_assoc
.reserved1
= 0;
68 rxon_assoc
.reserved2
= 0;
69 rxon_assoc
.reserved3
= 0;
70 rxon_assoc
.ofdm_ht_single_stream_basic_rates
=
71 priv
->staging_rxon
.ofdm_ht_single_stream_basic_rates
;
72 rxon_assoc
.ofdm_ht_dual_stream_basic_rates
=
73 priv
->staging_rxon
.ofdm_ht_dual_stream_basic_rates
;
74 rxon_assoc
.rx_chain_select_flags
= priv
->staging_rxon
.rx_chain
;
75 rxon_assoc
.ofdm_ht_triple_stream_basic_rates
=
76 priv
->staging_rxon
.ofdm_ht_triple_stream_basic_rates
;
77 rxon_assoc
.acquisition_data
= priv
->staging_rxon
.acquisition_data
;
79 ret
= iwl_send_cmd_pdu_async(priv
, REPLY_RXON_ASSOC
,
80 sizeof(rxon_assoc
), &rxon_assoc
, NULL
);
87 static int iwlagn_send_tx_ant_config(struct iwl_priv
*priv
, u8 valid_tx_ant
)
89 struct iwl_tx_ant_config_cmd tx_ant_cmd
= {
90 .valid
= cpu_to_le32(valid_tx_ant
),
93 if (IWL_UCODE_API(priv
->ucode_ver
) > 1) {
94 IWL_DEBUG_HC(priv
, "select valid tx ant: %u\n", valid_tx_ant
);
95 return iwl_send_cmd_pdu(priv
, TX_ANT_CONFIGURATION_CMD
,
96 sizeof(struct iwl_tx_ant_config_cmd
),
99 IWL_DEBUG_HC(priv
, "TX_ANT_CONFIGURATION_CMD not supported\n");
104 /* Currently this is the superset of everything */
105 static u16
iwlagn_get_hcmd_size(u8 cmd_id
, u16 len
)
110 static u16
iwlagn_build_addsta_hcmd(const struct iwl_addsta_cmd
*cmd
, u8
*data
)
112 u16 size
= (u16
)sizeof(struct iwl_addsta_cmd
);
113 struct iwl_addsta_cmd
*addsta
= (struct iwl_addsta_cmd
*)data
;
114 memcpy(addsta
, cmd
, size
);
115 /* resrved in 5000 */
116 addsta
->rate_n_flags
= cpu_to_le16(0);
120 static void iwlagn_gain_computation(struct iwl_priv
*priv
,
121 u32 average_noise
[NUM_RX_CHAINS
],
122 u16 min_average_noise_antenna_i
,
123 u32 min_average_noise
,
128 struct iwl_chain_noise_data
*data
= &priv
->chain_noise_data
;
131 * Find Gain Code for the chains based on "default chain"
133 for (i
= default_chain
+ 1; i
< NUM_RX_CHAINS
; i
++) {
134 if ((data
->disconn_array
[i
])) {
135 data
->delta_gain_code
[i
] = 0;
139 delta_g
= (priv
->cfg
->chain_noise_scale
*
140 ((s32
)average_noise
[default_chain
] -
141 (s32
)average_noise
[i
])) / 1500;
143 /* bound gain by 2 bits value max, 3rd bit is sign */
144 data
->delta_gain_code
[i
] =
145 min(abs(delta_g
), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE
);
149 * set negative sign ...
150 * note to Intel developers: This is uCode API format,
151 * not the format of any internal device registers.
152 * Do not change this format for e.g. 6050 or similar
153 * devices. Change format only if more resolution
154 * (i.e. more than 2 bits magnitude) is needed.
156 data
->delta_gain_code
[i
] |= (1 << 2);
159 IWL_DEBUG_CALIB(priv
, "Delta gains: ANT_B = %d ANT_C = %d\n",
160 data
->delta_gain_code
[1], data
->delta_gain_code
[2]);
162 if (!data
->radio_write
) {
163 struct iwl_calib_chain_noise_gain_cmd cmd
;
165 memset(&cmd
, 0, sizeof(cmd
));
167 cmd
.hdr
.op_code
= IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD
;
168 cmd
.hdr
.first_group
= 0;
169 cmd
.hdr
.groups_num
= 1;
170 cmd
.hdr
.data_valid
= 1;
171 cmd
.delta_gain_1
= data
->delta_gain_code
[1];
172 cmd
.delta_gain_2
= data
->delta_gain_code
[2];
173 iwl_send_cmd_pdu_async(priv
, REPLY_PHY_CALIBRATION_CMD
,
174 sizeof(cmd
), &cmd
, NULL
);
176 data
->radio_write
= 1;
177 data
->state
= IWL_CHAIN_NOISE_CALIBRATED
;
180 data
->chain_noise_a
= 0;
181 data
->chain_noise_b
= 0;
182 data
->chain_noise_c
= 0;
183 data
->chain_signal_a
= 0;
184 data
->chain_signal_b
= 0;
185 data
->chain_signal_c
= 0;
186 data
->beacon_count
= 0;
189 static void iwlagn_chain_noise_reset(struct iwl_priv
*priv
)
191 struct iwl_chain_noise_data
*data
= &priv
->chain_noise_data
;
194 if ((data
->state
== IWL_CHAIN_NOISE_ALIVE
) && iwl_is_associated(priv
)) {
195 struct iwl_calib_chain_noise_reset_cmd cmd
;
196 memset(&cmd
, 0, sizeof(cmd
));
198 cmd
.hdr
.op_code
= IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD
;
199 cmd
.hdr
.first_group
= 0;
200 cmd
.hdr
.groups_num
= 1;
201 cmd
.hdr
.data_valid
= 1;
202 ret
= iwl_send_cmd_pdu(priv
, REPLY_PHY_CALIBRATION_CMD
,
206 "Could not send REPLY_PHY_CALIBRATION_CMD\n");
207 data
->state
= IWL_CHAIN_NOISE_ACCUMULATE
;
208 IWL_DEBUG_CALIB(priv
, "Run chain_noise_calibrate\n");
212 static void iwlagn_rts_tx_cmd_flag(struct ieee80211_tx_info
*info
,
215 *tx_flags
|= TX_CMD_FLG_RTS_CTS_MSK
;
218 /* Calc max signal level (dBm) among 3 possible receivers */
219 static int iwlagn_calc_rssi(struct iwl_priv
*priv
,
220 struct iwl_rx_phy_res
*rx_resp
)
222 /* data from PHY/DSP regarding signal strength, etc.,
223 * contents are always there, not configurable by host
225 struct iwl5000_non_cfg_phy
*ncphy
=
226 (struct iwl5000_non_cfg_phy
*)rx_resp
->non_cfg_phy_buf
;
227 u32 val
, rssi_a
, rssi_b
, rssi_c
, max_rssi
;
230 val
= le32_to_cpu(ncphy
->non_cfg_phy
[IWL50_RX_RES_AGC_IDX
]);
231 agc
= (val
& IWL50_OFDM_AGC_MSK
) >> IWL50_OFDM_AGC_BIT_POS
;
233 /* Find max rssi among 3 possible receivers.
234 * These values are measured by the digital signal processor (DSP).
235 * They should stay fairly constant even as the signal strength varies,
236 * if the radio's automatic gain control (AGC) is working right.
237 * AGC value (see below) will provide the "interesting" info.
239 val
= le32_to_cpu(ncphy
->non_cfg_phy
[IWL50_RX_RES_RSSI_AB_IDX
]);
240 rssi_a
= (val
& IWL50_OFDM_RSSI_A_MSK
) >> IWL50_OFDM_RSSI_A_BIT_POS
;
241 rssi_b
= (val
& IWL50_OFDM_RSSI_B_MSK
) >> IWL50_OFDM_RSSI_B_BIT_POS
;
242 val
= le32_to_cpu(ncphy
->non_cfg_phy
[IWL50_RX_RES_RSSI_C_IDX
]);
243 rssi_c
= (val
& IWL50_OFDM_RSSI_C_MSK
) >> IWL50_OFDM_RSSI_C_BIT_POS
;
245 max_rssi
= max_t(u32
, rssi_a
, rssi_b
);
246 max_rssi
= max_t(u32
, max_rssi
, rssi_c
);
248 IWL_DEBUG_STATS(priv
, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
249 rssi_a
, rssi_b
, rssi_c
, max_rssi
, agc
);
251 /* dBm = max_rssi dB - agc dB - constant.
252 * Higher AGC (higher radio gain) means lower signal. */
253 return max_rssi
- agc
- IWLAGN_RSSI_OFFSET
;
256 struct iwl_hcmd_ops iwlagn_hcmd
= {
257 .rxon_assoc
= iwlagn_send_rxon_assoc
,
258 .commit_rxon
= iwl_commit_rxon
,
259 .set_rxon_chain
= iwl_set_rxon_chain
,
260 .set_tx_ant
= iwlagn_send_tx_ant_config
,
261 .send_bt_config
= iwl_send_bt_config
,
264 struct iwl_hcmd_utils_ops iwlagn_hcmd_utils
= {
265 .get_hcmd_size
= iwlagn_get_hcmd_size
,
266 .build_addsta_hcmd
= iwlagn_build_addsta_hcmd
,
267 .gain_computation
= iwlagn_gain_computation
,
268 .chain_noise_reset
= iwlagn_chain_noise_reset
,
269 .rts_tx_cmd_flag
= iwlagn_rts_tx_cmd_flag
,
270 .calc_rssi
= iwlagn_calc_rssi
,
271 .request_scan
= iwlagn_request_scan
,