aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-10-23 12:15:41 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-11-15 13:24:53 -0500
commit2295c66b68ae160dde2e6e2dc4f3061105153bfc (patch)
tree9bfbaf309944d5f72bd36b291680ddf370733c1a /drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
parent2d4e43c3c6783f956163c11568303b0390725e28 (diff)
iwlagn: new RXON processing for modern devices
In order to simplify the flow, and make new enhancements easier, separate out the RXON processing for modern AGN (5000 and newer) from RXON processing for the older 3945 and 4965 devices. Avoid changing these old ones to avoid regressions and move their code to a new file (iwl-legacy.c). 4965 gets the commit_rxon that used to be common for all AGN devices, but with removed PAN support. The new RXON processing is more central and does more work in committing, so that it is easier to follow. To make it more evident what is split out for legacy, split the necessary operations for that into a new struct iwl_legacy_ops. Those parts that still exist in the new AGN code don't need to be parametrized. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-rxon.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rxon.c562
1 files changed, 562 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
new file mode 100644
index 000000000000..58602457e415
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
@@ -0,0 +1,562 @@
1/******************************************************************************
2 *
3 * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
17 *
18 * The full GNU General Public License is included in this distribution in the
19 * file called LICENSE.
20 *
21 * Contact Information:
22 * Intel Linux Wireless <ilw@linux.intel.com>
23 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
24 *
25 *****************************************************************************/
26
27#include "iwl-dev.h"
28#include "iwl-agn.h"
29#include "iwl-sta.h"
30#include "iwl-core.h"
31#include "iwl-agn-calib.h"
32
33static int iwlagn_disable_bss(struct iwl_priv *priv,
34 struct iwl_rxon_context *ctx,
35 struct iwl_rxon_cmd *send)
36{
37 __le32 old_filter = send->filter_flags;
38 int ret;
39
40 send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
41 ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, sizeof(*send), send);
42
43 send->filter_flags = old_filter;
44
45 if (ret)
46 IWL_ERR(priv, "Error clearing ASSOC_MSK on BSS (%d)\n", ret);
47
48 return ret;
49}
50
51static int iwlagn_disable_pan(struct iwl_priv *priv,
52 struct iwl_rxon_context *ctx,
53 struct iwl_rxon_cmd *send)
54{
55 __le32 old_filter = send->filter_flags;
56 u8 old_dev_type = send->dev_type;
57 int ret;
58
59 send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
60 send->dev_type = RXON_DEV_TYPE_P2P;
61 ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, sizeof(*send), send);
62
63 send->filter_flags = old_filter;
64 send->dev_type = old_dev_type;
65
66 if (ret)
67 IWL_ERR(priv, "Error disabling PAN (%d)\n", ret);
68
69 /* FIXME: WAIT FOR PAN DISABLE */
70 msleep(300);
71
72 return ret;
73}
74
75/**
76 * iwlagn_commit_rxon - commit staging_rxon to hardware
77 *
78 * The RXON command in staging_rxon is committed to the hardware and
79 * the active_rxon structure is updated with the new data. This
80 * function correctly transitions out of the RXON_ASSOC_MSK state if
81 * a HW tune is required based on the RXON structure changes.
82 */
83int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
84{
85 /* cast away the const for active_rxon in this function */
86 struct iwl_rxon_cmd *active = (void *)&ctx->active;
87 bool old_assoc = !!(ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK);
88 bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK);
89 int ret;
90
91 lockdep_assert_held(&priv->mutex);
92
93 if (!iwl_is_alive(priv))
94 return -EBUSY;
95
96 /* This function hardcodes a bunch of dual-mode assumptions */
97 BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
98
99 if (!ctx->is_active)
100 return 0;
101
102 /* always get timestamp with Rx frame */
103 ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK;
104
105 if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
106 !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
107 ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
108 else
109 ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK;
110
111 ret = iwl_check_rxon_cmd(priv, ctx);
112 if (ret) {
113 IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n");
114 return -EINVAL;
115 }
116
117 /*
118 * receive commit_rxon request
119 * abort any previous channel switch if still in process
120 */
121 if (priv->switch_rxon.switch_in_progress &&
122 (priv->switch_rxon.channel != ctx->staging.channel)) {
123 IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
124 le16_to_cpu(priv->switch_rxon.channel));
125 iwl_chswitch_done(priv, false);
126 }
127
128 /*
129 * If we don't need to send a full RXON, we can use
130 * iwl_rxon_assoc_cmd which is used to reconfigure filter
131 * and other flags for the current radio configuration.
132 */
133 if (!iwl_full_rxon_required(priv, ctx)) {
134 ret = iwl_send_rxon_assoc(priv, ctx);
135 if (ret) {
136 IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret);
137 return ret;
138 }
139
140 memcpy(active, &ctx->staging, sizeof(*active));
141 iwl_print_rx_config_cmd(priv, ctx);
142 return 0;
143 }
144
145 if (priv->cfg->ops->hcmd->set_pan_params) {
146 ret = priv->cfg->ops->hcmd->set_pan_params(priv);
147 if (ret)
148 return ret;
149 }
150
151 iwl_set_rxon_hwcrypto(priv, ctx, !priv->cfg->mod_params->sw_crypto);
152
153 IWL_DEBUG_INFO(priv,
154 "Going to commit RXON\n"
155 " * with%s RXON_FILTER_ASSOC_MSK\n"
156 " * channel = %d\n"
157 " * bssid = %pM\n",
158 (new_assoc ? "" : "out"),
159 le16_to_cpu(ctx->staging.channel),
160 ctx->staging.bssid_addr);
161
162 /*
163 * If we are currently associated and the new config is also
164 * going to be associated, OR if the new config is simply not
165 * associated, clear associated.
166 */
167 if ((old_assoc && new_assoc) || !new_assoc) {
168 struct iwl_rxon_cmd *send = active;
169
170 if (!new_assoc)
171 send = &ctx->staging;
172
173 if (ctx->ctxid == IWL_RXON_CTX_BSS)
174 ret = iwlagn_disable_bss(priv, ctx, send);
175 else
176 ret = iwlagn_disable_pan(priv, ctx, send);
177 if (ret)
178 return ret;
179
180 if (send != active)
181 memcpy(active, send, sizeof(*active));
182
183 /*
184 * Un-assoc RXON clears the station table and WEP
185 * keys, so we have to restore those afterwards.
186 */
187 iwl_clear_ucode_stations(priv, ctx);
188 iwl_restore_stations(priv, ctx);
189 ret = iwl_restore_default_wep_keys(priv, ctx);
190 if (ret) {
191 IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret);
192 return ret;
193 }
194 }
195
196 /* RXON timing must be before associated RXON */
197 ret = iwl_send_rxon_timing(priv, ctx);
198 if (ret) {
199 IWL_ERR(priv, "Failed to send timing (%d)!\n", ret);
200 return ret;
201 }
202
203 if (new_assoc) {
204 if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP ||
205 ctx->vif->type == NL80211_IFTYPE_ADHOC)) {
206 /*
207 * We'll run into this code path when beaconing is
208 * enabled, but then we also need to send the beacon
209 * to the device.
210 */
211 dev_kfree_skb(priv->beacon_skb);
212 priv->beacon_skb = ieee80211_beacon_get(priv->hw,
213 ctx->vif);
214 iwlagn_send_beacon_cmd(priv);
215 }
216
217 priv->start_calib = 0;
218 /*
219 * Apply the new configuration.
220 *
221 * Associated RXON doesn't clear the station table in uCode,
222 * so we don't need to restore stations etc. after this.
223 */
224 ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd,
225 sizeof(struct iwl_rxon_cmd), &ctx->staging);
226 if (ret) {
227 IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
228 return ret;
229 }
230 memcpy(active, &ctx->staging, sizeof(*active));
231 }
232
233 iwl_print_rx_config_cmd(priv, ctx);
234
235 iwl_init_sensitivity(priv);
236
237 /*
238 * If we issue a new RXON command which required a tune then we must
239 * send a new TXPOWER command or we won't be able to Tx any frames.
240 *
241 * FIXME: which RXON requires a tune? Can we optimise this out in
242 * some cases?
243 */
244 ret = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true);
245 if (ret) {
246 IWL_ERR(priv, "Error sending TX power (%d)\n", ret);
247 return ret;
248 }
249
250 return 0;
251}
252
253int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
254{
255 struct iwl_priv *priv = hw->priv;
256 struct iwl_rxon_context *ctx;
257 struct ieee80211_conf *conf = &hw->conf;
258 struct ieee80211_channel *channel = conf->channel;
259 const struct iwl_channel_info *ch_info;
260 int ret = 0;
261
262 IWL_DEBUG_MAC80211(priv, "changed %#x", changed);
263
264 mutex_lock(&priv->mutex);
265
266 if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) {
267 IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
268 goto out;
269 }
270
271 if (!iwl_is_ready(priv)) {
272 IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
273 goto out;
274 }
275
276 if (changed & (IEEE80211_CONF_CHANGE_SMPS |
277 IEEE80211_CONF_CHANGE_CHANNEL)) {
278 /* mac80211 uses static for non-HT which is what we want */
279 priv->current_ht_config.smps = conf->smps_mode;
280
281 /*
282 * Recalculate chain counts.
283 *
284 * If monitor mode is enabled then mac80211 will
285 * set up the SM PS mode to OFF if an HT channel is
286 * configured.
287 */
288 if (priv->cfg->ops->hcmd->set_rxon_chain)
289 for_each_context(priv, ctx)
290 priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
291 }
292
293 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
294 unsigned long flags;
295
296 ch_info = iwl_get_channel_info(priv, channel->band,
297 channel->hw_value);
298 if (!is_channel_valid(ch_info)) {
299 IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
300 ret = -EINVAL;
301 goto out;
302 }
303
304 spin_lock_irqsave(&priv->lock, flags);
305
306 for_each_context(priv, ctx) {
307 /* Configure HT40 channels */
308 ctx->ht.enabled = conf_is_ht(conf);
309 if (ctx->ht.enabled) {
310 if (conf_is_ht40_minus(conf)) {
311 ctx->ht.extension_chan_offset =
312 IEEE80211_HT_PARAM_CHA_SEC_BELOW;
313 ctx->ht.is_40mhz = true;
314 } else if (conf_is_ht40_plus(conf)) {
315 ctx->ht.extension_chan_offset =
316 IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
317 ctx->ht.is_40mhz = true;
318 } else {
319 ctx->ht.extension_chan_offset =
320 IEEE80211_HT_PARAM_CHA_SEC_NONE;
321 ctx->ht.is_40mhz = false;
322 }
323 } else
324 ctx->ht.is_40mhz = false;
325
326 /*
327 * Default to no protection. Protection mode will
328 * later be set from BSS config in iwl_ht_conf
329 */
330 ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
331
332 /* if we are switching from ht to 2.4 clear flags
333 * from any ht related info since 2.4 does not
334 * support ht */
335 if (le16_to_cpu(ctx->staging.channel) !=
336 channel->hw_value)
337 ctx->staging.flags = 0;
338
339 iwl_set_rxon_channel(priv, channel, ctx);
340 iwl_set_rxon_ht(priv, &priv->current_ht_config);
341
342 iwl_set_flags_for_band(priv, ctx, channel->band,
343 ctx->vif);
344 }
345
346 spin_unlock_irqrestore(&priv->lock, flags);
347
348 iwl_update_bcast_stations(priv);
349
350 /*
351 * The list of supported rates and rate mask can be different
352 * for each band; since the band may have changed, reset
353 * the rate mask to what mac80211 lists.
354 */
355 iwl_set_rate(priv);
356 }
357
358 if (changed & (IEEE80211_CONF_CHANGE_PS |
359 IEEE80211_CONF_CHANGE_IDLE)) {
360 ret = iwl_power_update_mode(priv, false);
361 if (ret)
362 IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
363 }
364
365 if (changed & IEEE80211_CONF_CHANGE_POWER) {
366 IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
367 priv->tx_power_user_lmt, conf->power_level);
368
369 iwl_set_tx_power(priv, conf->power_level, false);
370 }
371
372 for_each_context(priv, ctx) {
373 if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
374 continue;
375 iwlagn_commit_rxon(priv, ctx);
376 }
377 out:
378 mutex_unlock(&priv->mutex);
379 return ret;
380}
381
382static void iwlagn_update_qos(struct iwl_priv *priv,
383 struct iwl_rxon_context *ctx)
384{
385 int ret;
386
387 if (!ctx->is_active)
388 return;
389
390 ctx->qos_data.def_qos_parm.qos_flags = 0;
391
392 if (ctx->qos_data.qos_active)
393 ctx->qos_data.def_qos_parm.qos_flags |=
394 QOS_PARAM_FLG_UPDATE_EDCA_MSK;
395
396 if (ctx->ht.enabled)
397 ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
398
399 IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
400 ctx->qos_data.qos_active,
401 ctx->qos_data.def_qos_parm.qos_flags);
402
403 ret = iwl_send_cmd_pdu(priv, ctx->qos_cmd,
404 sizeof(struct iwl_qosparam_cmd),
405 &ctx->qos_data.def_qos_parm);
406 if (ret)
407 IWL_ERR(priv, "Failed to update QoS\n");
408}
409
410static void iwlagn_check_needed_chains(struct iwl_priv *priv,
411 struct iwl_rxon_context *ctx,
412 struct ieee80211_bss_conf *bss_conf)
413{
414 struct ieee80211_vif *vif = ctx->vif;
415 struct iwl_rxon_context *tmp;
416 struct ieee80211_sta *sta;
417 struct iwl_ht_config *ht_conf = &priv->current_ht_config;
418 bool need_multiple;
419
420 lockdep_assert_held(&priv->mutex);
421
422 switch (vif->type) {
423 case NL80211_IFTYPE_STATION:
424 rcu_read_lock();
425 sta = ieee80211_find_sta(vif, bss_conf->bssid);
426 if (sta) {
427 struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
428 int maxstreams;
429
430 maxstreams = (ht_cap->mcs.tx_params &
431 IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
432 >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
433 maxstreams += 1;
434
435 if ((ht_cap->mcs.rx_mask[1] == 0) &&
436 (ht_cap->mcs.rx_mask[2] == 0))
437 need_multiple = false;
438 if (maxstreams <= 1)
439 need_multiple = true;
440 } else {
441 /*
442 * If at all, this can only happen through a race
443 * when the AP disconnects us while we're still
444 * setting up the connection, in that case mac80211
445 * will soon tell us about that.
446 */
447 need_multiple = false;
448 }
449 rcu_read_unlock();
450 break;
451 case NL80211_IFTYPE_ADHOC:
452 /* currently */
453 need_multiple = false;
454 break;
455 default:
456 /* only AP really */
457 need_multiple = true;
458 break;
459 }
460
461 ctx->ht_need_multiple_chains = need_multiple;
462
463 if (!need_multiple) {
464 /* check all contexts */
465 for_each_context(priv, tmp) {
466 if (!tmp->vif)
467 continue;
468 if (tmp->ht_need_multiple_chains) {
469 need_multiple = true;
470 break;
471 }
472 }
473 }
474
475 ht_conf->single_chain_sufficient = !need_multiple;
476}
477
478void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
479 struct ieee80211_vif *vif,
480 struct ieee80211_bss_conf *bss_conf,
481 u32 changes)
482{
483 struct iwl_priv *priv = hw->priv;
484 struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
485 int ret;
486 bool force = false;
487
488 mutex_lock(&priv->mutex);
489
490 if (changes & BSS_CHANGED_BEACON_INT)
491 force = true;
492
493 if (changes & BSS_CHANGED_QOS) {
494 ctx->qos_data.qos_active = bss_conf->qos;
495 iwlagn_update_qos(priv, ctx);
496 }
497
498 ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid);
499 if (vif->bss_conf.use_short_preamble)
500 ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
501 else
502 ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
503
504 if (changes & BSS_CHANGED_ASSOC) {
505 if (bss_conf->assoc) {
506 iwl_led_associate(priv);
507 priv->timestamp = bss_conf->timestamp;
508 ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
509 } else {
510 ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
511 iwl_led_disassociate(priv);
512 }
513 }
514
515 if (ctx->ht.enabled) {
516 ctx->ht.protection = bss_conf->ht_operation_mode &
517 IEEE80211_HT_OP_MODE_PROTECTION;
518 ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode &
519 IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
520 iwlagn_check_needed_chains(priv, ctx, bss_conf);
521 }
522
523 if (priv->cfg->ops->hcmd->set_rxon_chain)
524 priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
525
526 if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ))
527 ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
528 else
529 ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
530
531 if (bss_conf->use_cts_prot)
532 ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
533 else
534 ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
535
536 memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
537
538 if (vif->type == NL80211_IFTYPE_AP ||
539 vif->type == NL80211_IFTYPE_ADHOC) {
540 if (vif->bss_conf.enable_beacon) {
541 ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
542 priv->beacon_ctx = ctx;
543 } else {
544 ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
545 priv->beacon_ctx = NULL;
546 }
547 }
548
549 if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging)))
550 iwlagn_commit_rxon(priv, ctx);
551
552 if (changes & BSS_CHANGED_IBSS) {
553 ret = iwlagn_manage_ibss_station(priv, vif,
554 bss_conf->ibss_joined);
555 if (ret)
556 IWL_ERR(priv, "failed to %s IBSS station %pM\n",
557 bss_conf->ibss_joined ? "add" : "remove",
558 bss_conf->bssid);
559 }
560
561 mutex_unlock(&priv->mutex);
562}