f49e500cba67501cd44534137bf83f3084596c8c
[linux-2.6.git] / drivers / net / wireless / iwlwifi / iwl-legacy.c
1 /******************************************************************************
2  *
3  * GPL LICENSE SUMMARY
4  *
5  * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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,
19  * USA
20  *
21  * The full GNU General Public License is included in this distribution
22  * in the file called LICENSE.GPL.
23  *
24  * Contact Information:
25  *  Intel Linux Wireless <ilw@linux.intel.com>
26  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
27  *****************************************************************************/
28
29 #include <linux/kernel.h>
30 #include <net/mac80211.h>
31
32 #include "iwl-dev.h"
33 #include "iwl-core.h"
34 #include "iwl-helpers.h"
35 #include "iwl-legacy.h"
36
37 /**
38  * iwl_legacy_mac_config - mac80211 config callback
39  */
40 int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed)
41 {
42         struct iwl_priv *priv = hw->priv;
43         const struct iwl_channel_info *ch_info;
44         struct ieee80211_conf *conf = &hw->conf;
45         struct ieee80211_channel *channel = conf->channel;
46         struct iwl_ht_config *ht_conf = &priv->current_ht_config;
47         struct iwl_rxon_context *ctx;
48         unsigned long flags = 0;
49         int ret = 0;
50         u16 ch;
51         int scan_active = 0;
52
53         if (WARN_ON(!priv->cfg->ops->legacy))
54                 return -EOPNOTSUPP;
55
56         mutex_lock(&priv->mutex);
57
58         IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n",
59                                         channel->hw_value, changed);
60
61         if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
62                         test_bit(STATUS_SCANNING, &priv->status))) {
63                 scan_active = 1;
64                 IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
65         }
66
67         if (changed & (IEEE80211_CONF_CHANGE_SMPS |
68                        IEEE80211_CONF_CHANGE_CHANNEL)) {
69                 /* mac80211 uses static for non-HT which is what we want */
70                 priv->current_ht_config.smps = conf->smps_mode;
71
72                 /*
73                  * Recalculate chain counts.
74                  *
75                  * If monitor mode is enabled then mac80211 will
76                  * set up the SM PS mode to OFF if an HT channel is
77                  * configured.
78                  */
79                 if (priv->cfg->ops->hcmd->set_rxon_chain)
80                         for_each_context(priv, ctx)
81                                 priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
82         }
83
84         /* during scanning mac80211 will delay channel setting until
85          * scan finish with changed = 0
86          */
87         if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
88                 if (scan_active)
89                         goto set_ch_out;
90
91                 ch = channel->hw_value;
92                 ch_info = iwl_get_channel_info(priv, channel->band, ch);
93                 if (!is_channel_valid(ch_info)) {
94                         IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
95                         ret = -EINVAL;
96                         goto set_ch_out;
97                 }
98
99                 spin_lock_irqsave(&priv->lock, flags);
100
101                 for_each_context(priv, ctx) {
102                         /* Configure HT40 channels */
103                         ctx->ht.enabled = conf_is_ht(conf);
104                         if (ctx->ht.enabled) {
105                                 if (conf_is_ht40_minus(conf)) {
106                                         ctx->ht.extension_chan_offset =
107                                                 IEEE80211_HT_PARAM_CHA_SEC_BELOW;
108                                         ctx->ht.is_40mhz = true;
109                                 } else if (conf_is_ht40_plus(conf)) {
110                                         ctx->ht.extension_chan_offset =
111                                                 IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
112                                         ctx->ht.is_40mhz = true;
113                                 } else {
114                                         ctx->ht.extension_chan_offset =
115                                                 IEEE80211_HT_PARAM_CHA_SEC_NONE;
116                                         ctx->ht.is_40mhz = false;
117                                 }
118                         } else
119                                 ctx->ht.is_40mhz = false;
120
121                         /*
122                          * Default to no protection. Protection mode will
123                          * later be set from BSS config in iwl_ht_conf
124                          */
125                         ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
126
127                         /* if we are switching from ht to 2.4 clear flags
128                          * from any ht related info since 2.4 does not
129                          * support ht */
130                         if ((le16_to_cpu(ctx->staging.channel) != ch))
131                                 ctx->staging.flags = 0;
132
133                         iwl_set_rxon_channel(priv, channel, ctx);
134                         iwl_set_rxon_ht(priv, ht_conf);
135
136                         iwl_set_flags_for_band(priv, ctx, channel->band,
137                                                ctx->vif);
138                 }
139
140                 spin_unlock_irqrestore(&priv->lock, flags);
141
142                 if (priv->cfg->ops->legacy->update_bcast_stations)
143                         ret = priv->cfg->ops->legacy->update_bcast_stations(priv);
144
145  set_ch_out:
146                 /* The list of supported rates and rate mask can be different
147                  * for each band; since the band may have changed, reset
148                  * the rate mask to what mac80211 lists */
149                 iwl_set_rate(priv);
150         }
151
152         if (changed & (IEEE80211_CONF_CHANGE_PS |
153                         IEEE80211_CONF_CHANGE_IDLE)) {
154                 ret = iwl_power_update_mode(priv, false);
155                 if (ret)
156                         IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
157         }
158
159         if (changed & IEEE80211_CONF_CHANGE_POWER) {
160                 IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
161                         priv->tx_power_user_lmt, conf->power_level);
162
163                 iwl_set_tx_power(priv, conf->power_level, false);
164         }
165
166         if (!iwl_is_ready(priv)) {
167                 IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
168                 goto out;
169         }
170
171         if (scan_active)
172                 goto out;
173
174         for_each_context(priv, ctx) {
175                 if (memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)))
176                         iwlcore_commit_rxon(priv, ctx);
177                 else
178                         IWL_DEBUG_INFO(priv,
179                                 "Not re-sending same RXON configuration.\n");
180         }
181
182 out:
183         IWL_DEBUG_MAC80211(priv, "leave\n");
184         mutex_unlock(&priv->mutex);
185         return ret;
186 }
187 EXPORT_SYMBOL(iwl_legacy_mac_config);
188
189 void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw)
190 {
191         struct iwl_priv *priv = hw->priv;
192         unsigned long flags;
193         /* IBSS can only be the IWL_RXON_CTX_BSS context */
194         struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
195
196         if (WARN_ON(!priv->cfg->ops->legacy))
197                 return;
198
199         mutex_lock(&priv->mutex);
200         IWL_DEBUG_MAC80211(priv, "enter\n");
201
202         spin_lock_irqsave(&priv->lock, flags);
203         memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_config));
204         spin_unlock_irqrestore(&priv->lock, flags);
205
206         spin_lock_irqsave(&priv->lock, flags);
207
208         /* new association get rid of ibss beacon skb */
209         if (priv->beacon_skb)
210                 dev_kfree_skb(priv->beacon_skb);
211
212         priv->beacon_skb = NULL;
213
214         priv->timestamp = 0;
215
216         spin_unlock_irqrestore(&priv->lock, flags);
217
218         iwl_scan_cancel_timeout(priv, 100);
219         if (!iwl_is_ready_rf(priv)) {
220                 IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
221                 mutex_unlock(&priv->mutex);
222                 return;
223         }
224
225         /* we are restarting association process
226          * clear RXON_FILTER_ASSOC_MSK bit
227          */
228         ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
229         iwlcore_commit_rxon(priv, ctx);
230
231         iwl_set_rate(priv);
232
233         mutex_unlock(&priv->mutex);
234
235         IWL_DEBUG_MAC80211(priv, "leave\n");
236 }
237 EXPORT_SYMBOL(iwl_legacy_mac_reset_tsf);
238
239 static void iwl_ht_conf(struct iwl_priv *priv,
240                         struct ieee80211_vif *vif)
241 {
242         struct iwl_ht_config *ht_conf = &priv->current_ht_config;
243         struct ieee80211_sta *sta;
244         struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
245         struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
246
247         IWL_DEBUG_ASSOC(priv, "enter:\n");
248
249         if (!ctx->ht.enabled)
250                 return;
251
252         ctx->ht.protection =
253                 bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
254         ctx->ht.non_gf_sta_present =
255                 !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
256
257         ht_conf->single_chain_sufficient = false;
258
259         switch (vif->type) {
260         case NL80211_IFTYPE_STATION:
261                 rcu_read_lock();
262                 sta = ieee80211_find_sta(vif, bss_conf->bssid);
263                 if (sta) {
264                         struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
265                         int maxstreams;
266
267                         maxstreams = (ht_cap->mcs.tx_params &
268                                       IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
269                                         >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
270                         maxstreams += 1;
271
272                         if ((ht_cap->mcs.rx_mask[1] == 0) &&
273                             (ht_cap->mcs.rx_mask[2] == 0))
274                                 ht_conf->single_chain_sufficient = true;
275                         if (maxstreams <= 1)
276                                 ht_conf->single_chain_sufficient = true;
277                 } else {
278                         /*
279                          * If at all, this can only happen through a race
280                          * when the AP disconnects us while we're still
281                          * setting up the connection, in that case mac80211
282                          * will soon tell us about that.
283                          */
284                         ht_conf->single_chain_sufficient = true;
285                 }
286                 rcu_read_unlock();
287                 break;
288         case NL80211_IFTYPE_ADHOC:
289                 ht_conf->single_chain_sufficient = true;
290                 break;
291         default:
292                 break;
293         }
294
295         IWL_DEBUG_ASSOC(priv, "leave\n");
296 }
297
298 static void iwl_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
299 {
300         if (test_bit(STATUS_EXIT_PENDING, &priv->status))
301                 return;
302
303         if (!ctx->is_active)
304                 return;
305
306         ctx->qos_data.def_qos_parm.qos_flags = 0;
307
308         if (ctx->qos_data.qos_active)
309                 ctx->qos_data.def_qos_parm.qos_flags |=
310                         QOS_PARAM_FLG_UPDATE_EDCA_MSK;
311
312         if (ctx->ht.enabled)
313                 ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
314
315         IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
316                       ctx->qos_data.qos_active,
317                       ctx->qos_data.def_qos_parm.qos_flags);
318
319         iwl_send_cmd_pdu_async(priv, ctx->qos_cmd,
320                                sizeof(struct iwl_qosparam_cmd),
321                                &ctx->qos_data.def_qos_parm, NULL);
322 }
323
324 static inline void iwl_set_no_assoc(struct iwl_priv *priv,
325                                     struct ieee80211_vif *vif)
326 {
327         struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
328
329         iwl_led_disassociate(priv);
330         /*
331          * inform the ucode that there is no longer an
332          * association and that no more packets should be
333          * sent
334          */
335         ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
336         ctx->staging.assoc_id = 0;
337         iwlcore_commit_rxon(priv, ctx);
338 }
339
340 static void iwlcore_beacon_update(struct ieee80211_hw *hw,
341                                   struct ieee80211_vif *vif)
342 {
343         struct iwl_priv *priv = hw->priv;
344         unsigned long flags;
345         __le64 timestamp;
346         struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
347
348         if (!skb)
349                 return;
350
351         IWL_DEBUG_MAC80211(priv, "enter\n");
352
353         lockdep_assert_held(&priv->mutex);
354
355         if (!priv->beacon_ctx) {
356                 IWL_ERR(priv, "update beacon but no beacon context!\n");
357                 dev_kfree_skb(skb);
358                 return;
359         }
360
361         spin_lock_irqsave(&priv->lock, flags);
362
363         if (priv->beacon_skb)
364                 dev_kfree_skb(priv->beacon_skb);
365
366         priv->beacon_skb = skb;
367
368         timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
369         priv->timestamp = le64_to_cpu(timestamp);
370
371         IWL_DEBUG_MAC80211(priv, "leave\n");
372         spin_unlock_irqrestore(&priv->lock, flags);
373
374         if (!iwl_is_ready_rf(priv)) {
375                 IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
376                 return;
377         }
378
379         priv->cfg->ops->legacy->post_associate(priv);
380 }
381
382 void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw,
383                                      struct ieee80211_vif *vif,
384                                      struct ieee80211_bss_conf *bss_conf,
385                                      u32 changes)
386 {
387         struct iwl_priv *priv = hw->priv;
388         struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
389         int ret;
390
391         if (WARN_ON(!priv->cfg->ops->legacy))
392                 return;
393
394         IWL_DEBUG_MAC80211(priv, "changes = 0x%X\n", changes);
395
396         if (!iwl_is_alive(priv))
397                 return;
398
399         mutex_lock(&priv->mutex);
400
401         if (changes & BSS_CHANGED_QOS) {
402                 unsigned long flags;
403
404                 spin_lock_irqsave(&priv->lock, flags);
405                 ctx->qos_data.qos_active = bss_conf->qos;
406                 iwl_update_qos(priv, ctx);
407                 spin_unlock_irqrestore(&priv->lock, flags);
408         }
409
410         if (changes & BSS_CHANGED_BEACON_ENABLED) {
411                 /*
412                  * the add_interface code must make sure we only ever
413                  * have a single interface that could be beaconing at
414                  * any time.
415                  */
416                 if (vif->bss_conf.enable_beacon)
417                         priv->beacon_ctx = ctx;
418                 else
419                         priv->beacon_ctx = NULL;
420         }
421
422         if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_AP) {
423                 dev_kfree_skb(priv->beacon_skb);
424                 priv->beacon_skb = ieee80211_beacon_get(hw, vif);
425         }
426
427         if (changes & BSS_CHANGED_BEACON_INT && vif->type == NL80211_IFTYPE_AP)
428                 iwl_send_rxon_timing(priv, ctx);
429
430         if (changes & BSS_CHANGED_BSSID) {
431                 IWL_DEBUG_MAC80211(priv, "BSSID %pM\n", bss_conf->bssid);
432
433                 /*
434                  * If there is currently a HW scan going on in the
435                  * background then we need to cancel it else the RXON
436                  * below/in post_associate will fail.
437                  */
438                 if (iwl_scan_cancel_timeout(priv, 100)) {
439                         IWL_WARN(priv, "Aborted scan still in progress after 100ms\n");
440                         IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n");
441                         mutex_unlock(&priv->mutex);
442                         return;
443                 }
444
445                 /* mac80211 only sets assoc when in STATION mode */
446                 if (vif->type == NL80211_IFTYPE_ADHOC || bss_conf->assoc) {
447                         memcpy(ctx->staging.bssid_addr,
448                                bss_conf->bssid, ETH_ALEN);
449
450                         /* currently needed in a few places */
451                         memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN);
452                 } else {
453                         ctx->staging.filter_flags &=
454                                 ~RXON_FILTER_ASSOC_MSK;
455                 }
456
457         }
458
459         /*
460          * This needs to be after setting the BSSID in case
461          * mac80211 decides to do both changes at once because
462          * it will invoke post_associate.
463          */
464         if (vif->type == NL80211_IFTYPE_ADHOC && changes & BSS_CHANGED_BEACON)
465                 iwlcore_beacon_update(hw, vif);
466
467         if (changes & BSS_CHANGED_ERP_PREAMBLE) {
468                 IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n",
469                                    bss_conf->use_short_preamble);
470                 if (bss_conf->use_short_preamble)
471                         ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
472                 else
473                         ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
474         }
475
476         if (changes & BSS_CHANGED_ERP_CTS_PROT) {
477                 IWL_DEBUG_MAC80211(priv, "ERP_CTS %d\n", bss_conf->use_cts_prot);
478                 if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ))
479                         ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
480                 else
481                         ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
482                 if (bss_conf->use_cts_prot)
483                         ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
484                 else
485                         ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
486         }
487
488         if (changes & BSS_CHANGED_BASIC_RATES) {
489                 /* XXX use this information
490                  *
491                  * To do that, remove code from iwl_set_rate() and put something
492                  * like this here:
493                  *
494                 if (A-band)
495                         ctx->staging.ofdm_basic_rates =
496                                 bss_conf->basic_rates;
497                 else
498                         ctx->staging.ofdm_basic_rates =
499                                 bss_conf->basic_rates >> 4;
500                         ctx->staging.cck_basic_rates =
501                                 bss_conf->basic_rates & 0xF;
502                  */
503         }
504
505         if (changes & BSS_CHANGED_HT) {
506                 iwl_ht_conf(priv, vif);
507
508                 if (priv->cfg->ops->hcmd->set_rxon_chain)
509                         priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
510         }
511
512         if (changes & BSS_CHANGED_ASSOC) {
513                 IWL_DEBUG_MAC80211(priv, "ASSOC %d\n", bss_conf->assoc);
514                 if (bss_conf->assoc) {
515                         priv->timestamp = bss_conf->timestamp;
516
517                         iwl_led_associate(priv);
518
519                         if (!iwl_is_rfkill(priv))
520                                 priv->cfg->ops->legacy->post_associate(priv);
521                 } else
522                         iwl_set_no_assoc(priv, vif);
523         }
524
525         if (changes && iwl_is_associated_ctx(ctx) && bss_conf->aid) {
526                 IWL_DEBUG_MAC80211(priv, "Changes (%#x) while associated\n",
527                                    changes);
528                 ret = iwl_send_rxon_assoc(priv, ctx);
529                 if (!ret) {
530                         /* Sync active_rxon with latest change. */
531                         memcpy((void *)&ctx->active,
532                                 &ctx->staging,
533                                 sizeof(struct iwl_rxon_cmd));
534                 }
535         }
536
537         if (changes & BSS_CHANGED_BEACON_ENABLED) {
538                 if (vif->bss_conf.enable_beacon) {
539                         memcpy(ctx->staging.bssid_addr,
540                                bss_conf->bssid, ETH_ALEN);
541                         memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN);
542                         iwl_led_associate(priv);
543                         priv->cfg->ops->legacy->config_ap(priv);
544                 } else
545                         iwl_set_no_assoc(priv, vif);
546         }
547
548         if (changes & BSS_CHANGED_IBSS) {
549                 ret = priv->cfg->ops->legacy->manage_ibss_station(priv, vif,
550                                                         bss_conf->ibss_joined);
551                 if (ret)
552                         IWL_ERR(priv, "failed to %s IBSS station %pM\n",
553                                 bss_conf->ibss_joined ? "add" : "remove",
554                                 bss_conf->bssid);
555         }
556
557         mutex_unlock(&priv->mutex);
558
559         IWL_DEBUG_MAC80211(priv, "leave\n");
560 }
561 EXPORT_SYMBOL(iwl_legacy_mac_bss_info_changed);
562
563 irqreturn_t iwl_isr_legacy(int irq, void *data)
564 {
565         struct iwl_priv *priv = data;
566         u32 inta, inta_mask;
567         u32 inta_fh;
568         unsigned long flags;
569         if (!priv)
570                 return IRQ_NONE;
571
572         spin_lock_irqsave(&priv->lock, flags);
573
574         /* Disable (but don't clear!) interrupts here to avoid
575          *    back-to-back ISRs and sporadic interrupts from our NIC.
576          * If we have something to service, the tasklet will re-enable ints.
577          * If we *don't* have something, we'll re-enable before leaving here. */
578         inta_mask = iwl_read32(priv, CSR_INT_MASK);  /* just for debug */
579         iwl_write32(priv, CSR_INT_MASK, 0x00000000);
580
581         /* Discover which interrupts are active/pending */
582         inta = iwl_read32(priv, CSR_INT);
583         inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
584
585         /* Ignore interrupt if there's nothing in NIC to service.
586          * This may be due to IRQ shared with another device,
587          * or due to sporadic interrupts thrown from our NIC. */
588         if (!inta && !inta_fh) {
589                 IWL_DEBUG_ISR(priv,
590                         "Ignore interrupt, inta == 0, inta_fh == 0\n");
591                 goto none;
592         }
593
594         if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
595                 /* Hardware disappeared. It might have already raised
596                  * an interrupt */
597                 IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
598                 goto unplugged;
599         }
600
601         IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
602                       inta, inta_mask, inta_fh);
603
604         inta &= ~CSR_INT_BIT_SCD;
605
606         /* iwl_irq_tasklet() will service interrupts and re-enable them */
607         if (likely(inta || inta_fh))
608                 tasklet_schedule(&priv->irq_tasklet);
609
610 unplugged:
611         spin_unlock_irqrestore(&priv->lock, flags);
612         return IRQ_HANDLED;
613
614 none:
615         /* re-enable interrupts here since we don't have anything to service. */
616         /* only Re-enable if diabled by irq */
617         if (test_bit(STATUS_INT_ENABLED, &priv->status))
618                 iwl_enable_interrupts(priv);
619         spin_unlock_irqrestore(&priv->lock, flags);
620         return IRQ_NONE;
621 }
622 EXPORT_SYMBOL(iwl_isr_legacy);