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 int iwlagn_send_rxon_assoc(struct iwl_priv
*priv
,
41 struct iwl_rxon_context
*ctx
)
44 struct iwl5000_rxon_assoc_cmd rxon_assoc
;
45 const struct iwl_rxon_cmd
*rxon1
= &ctx
->staging
;
46 const struct iwl_rxon_cmd
*rxon2
= &ctx
->active
;
48 if ((rxon1
->flags
== rxon2
->flags
) &&
49 (rxon1
->filter_flags
== rxon2
->filter_flags
) &&
50 (rxon1
->cck_basic_rates
== rxon2
->cck_basic_rates
) &&
51 (rxon1
->ofdm_ht_single_stream_basic_rates
==
52 rxon2
->ofdm_ht_single_stream_basic_rates
) &&
53 (rxon1
->ofdm_ht_dual_stream_basic_rates
==
54 rxon2
->ofdm_ht_dual_stream_basic_rates
) &&
55 (rxon1
->ofdm_ht_triple_stream_basic_rates
==
56 rxon2
->ofdm_ht_triple_stream_basic_rates
) &&
57 (rxon1
->acquisition_data
== rxon2
->acquisition_data
) &&
58 (rxon1
->rx_chain
== rxon2
->rx_chain
) &&
59 (rxon1
->ofdm_basic_rates
== rxon2
->ofdm_basic_rates
)) {
60 IWL_DEBUG_INFO(priv
, "Using current RXON_ASSOC. Not resending.\n");
64 rxon_assoc
.flags
= ctx
->staging
.flags
;
65 rxon_assoc
.filter_flags
= ctx
->staging
.filter_flags
;
66 rxon_assoc
.ofdm_basic_rates
= ctx
->staging
.ofdm_basic_rates
;
67 rxon_assoc
.cck_basic_rates
= ctx
->staging
.cck_basic_rates
;
68 rxon_assoc
.reserved1
= 0;
69 rxon_assoc
.reserved2
= 0;
70 rxon_assoc
.reserved3
= 0;
71 rxon_assoc
.ofdm_ht_single_stream_basic_rates
=
72 ctx
->staging
.ofdm_ht_single_stream_basic_rates
;
73 rxon_assoc
.ofdm_ht_dual_stream_basic_rates
=
74 ctx
->staging
.ofdm_ht_dual_stream_basic_rates
;
75 rxon_assoc
.rx_chain_select_flags
= ctx
->staging
.rx_chain
;
76 rxon_assoc
.ofdm_ht_triple_stream_basic_rates
=
77 ctx
->staging
.ofdm_ht_triple_stream_basic_rates
;
78 rxon_assoc
.acquisition_data
= ctx
->staging
.acquisition_data
;
80 ret
= iwl_send_cmd_pdu_async(priv
, ctx
->rxon_assoc_cmd
,
81 sizeof(rxon_assoc
), &rxon_assoc
, NULL
);
88 int iwlagn_send_tx_ant_config(struct iwl_priv
*priv
, u8 valid_tx_ant
)
90 struct iwl_tx_ant_config_cmd tx_ant_cmd
= {
91 .valid
= cpu_to_le32(valid_tx_ant
),
94 if (IWL_UCODE_API(priv
->ucode_ver
) > 1) {
95 IWL_DEBUG_HC(priv
, "select valid tx ant: %u\n", valid_tx_ant
);
96 return iwl_send_cmd_pdu(priv
, TX_ANT_CONFIGURATION_CMD
,
97 sizeof(struct iwl_tx_ant_config_cmd
),
100 IWL_DEBUG_HC(priv
, "TX_ANT_CONFIGURATION_CMD not supported\n");
105 /* Currently this is the superset of everything */
106 static u16
iwlagn_get_hcmd_size(u8 cmd_id
, u16 len
)
111 static u16
iwlagn_build_addsta_hcmd(const struct iwl_addsta_cmd
*cmd
, u8
*data
)
113 u16 size
= (u16
)sizeof(struct iwl_addsta_cmd
);
114 struct iwl_addsta_cmd
*addsta
= (struct iwl_addsta_cmd
*)data
;
115 memcpy(addsta
, cmd
, size
);
116 /* resrved in 5000 */
117 addsta
->rate_n_flags
= cpu_to_le16(0);
121 static void iwlagn_gain_computation(struct iwl_priv
*priv
,
122 u32 average_noise
[NUM_RX_CHAINS
],
123 u16 min_average_noise_antenna_i
,
124 u32 min_average_noise
,
129 struct iwl_chain_noise_data
*data
= &priv
->chain_noise_data
;
132 * Find Gain Code for the chains based on "default chain"
134 for (i
= default_chain
+ 1; i
< NUM_RX_CHAINS
; i
++) {
135 if ((data
->disconn_array
[i
])) {
136 data
->delta_gain_code
[i
] = 0;
140 delta_g
= (priv
->cfg
->base_params
->chain_noise_scale
*
141 ((s32
)average_noise
[default_chain
] -
142 (s32
)average_noise
[i
])) / 1500;
144 /* bound gain by 2 bits value max, 3rd bit is sign */
145 data
->delta_gain_code
[i
] =
146 min(abs(delta_g
), (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE
);
150 * set negative sign ...
151 * note to Intel developers: This is uCode API format,
152 * not the format of any internal device registers.
153 * Do not change this format for e.g. 6050 or similar
154 * devices. Change format only if more resolution
155 * (i.e. more than 2 bits magnitude) is needed.
157 data
->delta_gain_code
[i
] |= (1 << 2);
160 IWL_DEBUG_CALIB(priv
, "Delta gains: ANT_B = %d ANT_C = %d\n",
161 data
->delta_gain_code
[1], data
->delta_gain_code
[2]);
163 if (!data
->radio_write
) {
164 struct iwl_calib_chain_noise_gain_cmd cmd
;
166 memset(&cmd
, 0, sizeof(cmd
));
168 cmd
.hdr
.op_code
= priv
->_agn
.phy_calib_chain_noise_gain_cmd
;
169 cmd
.hdr
.first_group
= 0;
170 cmd
.hdr
.groups_num
= 1;
171 cmd
.hdr
.data_valid
= 1;
172 cmd
.delta_gain_1
= data
->delta_gain_code
[1];
173 cmd
.delta_gain_2
= data
->delta_gain_code
[2];
174 iwl_send_cmd_pdu_async(priv
, REPLY_PHY_CALIBRATION_CMD
,
175 sizeof(cmd
), &cmd
, NULL
);
177 data
->radio_write
= 1;
178 data
->state
= IWL_CHAIN_NOISE_CALIBRATED
;
182 static void iwlagn_chain_noise_reset(struct iwl_priv
*priv
)
184 struct iwl_chain_noise_data
*data
= &priv
->chain_noise_data
;
187 if ((data
->state
== IWL_CHAIN_NOISE_ALIVE
) &&
188 iwl_is_any_associated(priv
)) {
189 struct iwl_calib_chain_noise_reset_cmd cmd
;
191 /* clear data for chain noise calibration algorithm */
192 data
->chain_noise_a
= 0;
193 data
->chain_noise_b
= 0;
194 data
->chain_noise_c
= 0;
195 data
->chain_signal_a
= 0;
196 data
->chain_signal_b
= 0;
197 data
->chain_signal_c
= 0;
198 data
->beacon_count
= 0;
200 memset(&cmd
, 0, sizeof(cmd
));
201 cmd
.hdr
.op_code
= priv
->_agn
.phy_calib_chain_noise_reset_cmd
;
202 cmd
.hdr
.first_group
= 0;
203 cmd
.hdr
.groups_num
= 1;
204 cmd
.hdr
.data_valid
= 1;
205 ret
= iwl_send_cmd_pdu(priv
, REPLY_PHY_CALIBRATION_CMD
,
209 "Could not send REPLY_PHY_CALIBRATION_CMD\n");
210 data
->state
= IWL_CHAIN_NOISE_ACCUMULATE
;
211 IWL_DEBUG_CALIB(priv
, "Run chain_noise_calibrate\n");
215 static void iwlagn_tx_cmd_protection(struct iwl_priv
*priv
,
216 struct ieee80211_tx_info
*info
,
217 __le16 fc
, __le32
*tx_flags
)
219 if (info
->control
.rates
[0].flags
& IEEE80211_TX_RC_USE_RTS_CTS
||
220 info
->control
.rates
[0].flags
& IEEE80211_TX_RC_USE_CTS_PROTECT
) {
221 *tx_flags
|= TX_CMD_FLG_PROT_REQUIRE_MSK
;
225 if (priv
->cfg
->ht_params
&&
226 priv
->cfg
->ht_params
->use_rts_for_aggregation
&&
227 info
->flags
& IEEE80211_TX_CTL_AMPDU
) {
228 *tx_flags
|= TX_CMD_FLG_PROT_REQUIRE_MSK
;
233 /* Calc max signal level (dBm) among 3 possible receivers */
234 static int iwlagn_calc_rssi(struct iwl_priv
*priv
,
235 struct iwl_rx_phy_res
*rx_resp
)
237 /* data from PHY/DSP regarding signal strength, etc.,
238 * contents are always there, not configurable by host
240 struct iwlagn_non_cfg_phy
*ncphy
=
241 (struct iwlagn_non_cfg_phy
*)rx_resp
->non_cfg_phy_buf
;
242 u32 val
, rssi_a
, rssi_b
, rssi_c
, max_rssi
;
245 val
= le32_to_cpu(ncphy
->non_cfg_phy
[IWLAGN_RX_RES_AGC_IDX
]);
246 agc
= (val
& IWLAGN_OFDM_AGC_MSK
) >> IWLAGN_OFDM_AGC_BIT_POS
;
248 /* Find max rssi among 3 possible receivers.
249 * These values are measured by the digital signal processor (DSP).
250 * They should stay fairly constant even as the signal strength varies,
251 * if the radio's automatic gain control (AGC) is working right.
252 * AGC value (see below) will provide the "interesting" info.
254 val
= le32_to_cpu(ncphy
->non_cfg_phy
[IWLAGN_RX_RES_RSSI_AB_IDX
]);
255 rssi_a
= (val
& IWLAGN_OFDM_RSSI_INBAND_A_BITMSK
) >>
256 IWLAGN_OFDM_RSSI_A_BIT_POS
;
257 rssi_b
= (val
& IWLAGN_OFDM_RSSI_INBAND_B_BITMSK
) >>
258 IWLAGN_OFDM_RSSI_B_BIT_POS
;
259 val
= le32_to_cpu(ncphy
->non_cfg_phy
[IWLAGN_RX_RES_RSSI_C_IDX
]);
260 rssi_c
= (val
& IWLAGN_OFDM_RSSI_INBAND_C_BITMSK
) >>
261 IWLAGN_OFDM_RSSI_C_BIT_POS
;
263 max_rssi
= max_t(u32
, rssi_a
, rssi_b
);
264 max_rssi
= max_t(u32
, max_rssi
, rssi_c
);
266 IWL_DEBUG_STATS(priv
, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
267 rssi_a
, rssi_b
, rssi_c
, max_rssi
, agc
);
269 /* dBm = max_rssi dB - agc dB - constant.
270 * Higher AGC (higher radio gain) means lower signal. */
271 return max_rssi
- agc
- IWLAGN_RSSI_OFFSET
;
274 static int iwlagn_set_pan_params(struct iwl_priv
*priv
)
276 struct iwl_wipan_params_cmd cmd
;
277 struct iwl_rxon_context
*ctx_bss
, *ctx_pan
;
278 int slot0
= 300, slot1
= 0;
281 if (priv
->valid_contexts
== BIT(IWL_RXON_CTX_BSS
))
284 BUILD_BUG_ON(NUM_IWL_RXON_CTX
!= 2);
286 lockdep_assert_held(&priv
->mutex
);
288 ctx_bss
= &priv
->contexts
[IWL_RXON_CTX_BSS
];
289 ctx_pan
= &priv
->contexts
[IWL_RXON_CTX_PAN
];
292 * If the PAN context is inactive, then we don't need
293 * to update the PAN parameters, the last thing we'll
294 * have done before it goes inactive is making the PAN
295 * parameters be WLAN-only.
297 if (!ctx_pan
->is_active
)
300 memset(&cmd
, 0, sizeof(cmd
));
302 /* only 2 slots are currently allowed */
305 cmd
.slots
[0].type
= 0; /* BSS */
306 cmd
.slots
[1].type
= 1; /* PAN */
308 if (ctx_bss
->vif
&& ctx_pan
->vif
) {
309 int bcnint
= ctx_pan
->vif
->bss_conf
.beacon_int
;
310 int dtim
= ctx_pan
->vif
->bss_conf
.dtim_period
?: 1;
312 /* should be set, but seems unused?? */
313 cmd
.flags
|= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE
);
315 if (ctx_pan
->vif
->type
== NL80211_IFTYPE_AP
&&
317 bcnint
!= ctx_bss
->vif
->bss_conf
.beacon_int
) {
319 "beacon intervals don't match (%d, %d)\n",
320 ctx_bss
->vif
->bss_conf
.beacon_int
,
321 ctx_pan
->vif
->bss_conf
.beacon_int
);
323 bcnint
= max_t(int, bcnint
,
324 ctx_bss
->vif
->bss_conf
.beacon_int
);
326 bcnint
= DEFAULT_BEACON_INTERVAL
;
328 slot1
= bcnint
- slot0
;
330 if (test_bit(STATUS_SCAN_HW
, &priv
->status
) ||
331 (!ctx_bss
->vif
->bss_conf
.idle
&&
332 !ctx_bss
->vif
->bss_conf
.assoc
)) {
333 slot0
= dtim
* bcnint
* 3 - 20;
335 } else if (!ctx_pan
->vif
->bss_conf
.idle
&&
336 !ctx_pan
->vif
->bss_conf
.assoc
) {
337 slot1
= bcnint
* 3 - 20;
340 } else if (ctx_pan
->vif
) {
342 slot1
= max_t(int, 1, ctx_pan
->vif
->bss_conf
.dtim_period
) *
343 ctx_pan
->vif
->bss_conf
.beacon_int
;
344 slot1
= max_t(int, DEFAULT_BEACON_INTERVAL
, slot1
);
346 if (test_bit(STATUS_SCAN_HW
, &priv
->status
)) {
347 slot0
= slot1
* 3 - 20;
352 cmd
.slots
[0].width
= cpu_to_le16(slot0
);
353 cmd
.slots
[1].width
= cpu_to_le16(slot1
);
355 ret
= iwl_send_cmd_pdu(priv
, REPLY_WIPAN_PARAMS
, sizeof(cmd
), &cmd
);
357 IWL_ERR(priv
, "Error setting PAN parameters (%d)\n", ret
);
362 struct iwl_hcmd_ops iwlagn_hcmd
= {
363 .rxon_assoc
= iwlagn_send_rxon_assoc
,
364 .commit_rxon
= iwlagn_commit_rxon
,
365 .set_rxon_chain
= iwlagn_set_rxon_chain
,
366 .set_tx_ant
= iwlagn_send_tx_ant_config
,
367 .send_bt_config
= iwl_send_bt_config
,
368 .set_pan_params
= iwlagn_set_pan_params
,
371 struct iwl_hcmd_ops iwlagn_bt_hcmd
= {
372 .rxon_assoc
= iwlagn_send_rxon_assoc
,
373 .commit_rxon
= iwlagn_commit_rxon
,
374 .set_rxon_chain
= iwlagn_set_rxon_chain
,
375 .set_tx_ant
= iwlagn_send_tx_ant_config
,
376 .send_bt_config
= iwlagn_send_advance_bt_config
,
377 .set_pan_params
= iwlagn_set_pan_params
,
380 struct iwl_hcmd_utils_ops iwlagn_hcmd_utils
= {
381 .get_hcmd_size
= iwlagn_get_hcmd_size
,
382 .build_addsta_hcmd
= iwlagn_build_addsta_hcmd
,
383 .gain_computation
= iwlagn_gain_computation
,
384 .chain_noise_reset
= iwlagn_chain_noise_reset
,
385 .tx_cmd_protection
= iwlagn_tx_cmd_protection
,
386 .calc_rssi
= iwlagn_calc_rssi
,
387 .request_scan
= iwlagn_request_scan
,
388 .post_scan
= iwlagn_post_scan
,