]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/iwlwifi/iwl-core.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / wireless / iwlwifi / iwl-core.c
index 25fb3912342ceff334f6c2d82cb61defc5b0ee75..efbde1f1a8bfca29924465c0c56ba57cf4c74e17 100644 (file)
@@ -77,15 +77,15 @@ EXPORT_SYMBOL(iwl_bcast_addr);
 
 
 /* This function both allocates and initializes hw and priv. */
-struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
-               struct ieee80211_ops *hw_ops)
+struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg)
 {
        struct iwl_priv *priv;
-
        /* mac80211 allocates memory for this device instance, including
         *   space for this driver's private structure */
-       struct ieee80211_hw *hw =
-               ieee80211_alloc_hw(sizeof(struct iwl_priv), hw_ops);
+       struct ieee80211_hw *hw;
+
+       hw = ieee80211_alloc_hw(sizeof(struct iwl_priv),
+                               cfg->ops->ieee80211_ops);
        if (hw == NULL) {
                pr_err("%s: Can not allocate network device\n",
                       cfg->name);
@@ -100,35 +100,6 @@ out:
 }
 EXPORT_SYMBOL(iwl_alloc_all);
 
-/*
- * QoS  support
-*/
-static void iwl_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
-       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
-               return;
-
-       if (!ctx->is_active)
-               return;
-
-       ctx->qos_data.def_qos_parm.qos_flags = 0;
-
-       if (ctx->qos_data.qos_active)
-               ctx->qos_data.def_qos_parm.qos_flags |=
-                       QOS_PARAM_FLG_UPDATE_EDCA_MSK;
-
-       if (ctx->ht.enabled)
-               ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
-
-       IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
-                     ctx->qos_data.qos_active,
-                     ctx->qos_data.def_qos_parm.qos_flags);
-
-       iwl_send_cmd_pdu_async(priv, ctx->qos_cmd,
-                              sizeof(struct iwl_qosparam_cmd),
-                              &ctx->qos_data.def_qos_parm, NULL);
-}
-
 #define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
 #define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
 static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
@@ -317,40 +288,6 @@ void iwlcore_free_geos(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwlcore_free_geos);
 
-/*
- *  iwlcore_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this
- *  function.
- */
-void iwlcore_tx_cmd_protection(struct iwl_priv *priv,
-                              struct ieee80211_tx_info *info,
-                              __le16 fc, __le32 *tx_flags)
-{
-       if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
-               *tx_flags |= TX_CMD_FLG_RTS_MSK;
-               *tx_flags &= ~TX_CMD_FLG_CTS_MSK;
-               *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
-
-               if (!ieee80211_is_mgmt(fc))
-                       return;
-
-               switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
-               case cpu_to_le16(IEEE80211_STYPE_AUTH):
-               case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
-               case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ):
-               case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ):
-                       *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
-                       *tx_flags |= TX_CMD_FLG_CTS_MSK;
-                       break;
-               }
-       } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-               *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
-               *tx_flags |= TX_CMD_FLG_CTS_MSK;
-               *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
-       }
-}
-EXPORT_SYMBOL(iwlcore_tx_cmd_protection);
-
-
 static bool iwl_is_channel_extension(struct iwl_priv *priv,
                                     enum ieee80211_band band,
                                     u16 channel, u8 extension_chan_offset)
@@ -1020,6 +957,22 @@ void iwl_irq_handle_error(struct iwl_priv *priv)
        /* Cancel currently queued command. */
        clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
 
+       /* W/A for WiFi/WiMAX coex and WiMAX own the RF */
+       if (priv->cfg->internal_wimax_coex &&
+           (!(iwl_read_prph(priv, APMG_CLK_CTRL_REG) &
+                       APMS_CLK_VAL_MRB_FUNC_MODE) ||
+            (iwl_read_prph(priv, APMG_PS_CTRL_REG) &
+                       APMG_PS_CTRL_VAL_RESET_REQ))) {
+               wake_up_interruptible(&priv->wait_command_queue);
+               /*
+                *Keep the restart process from trying to send host
+                * commands by clearing the INIT status bit
+                */
+               clear_bit(STATUS_READY, &priv->status);
+               IWL_ERR(priv, "RF is used by WiMAX\n");
+               return;
+       }
+
        IWL_ERR(priv, "Loaded firmware version: %s\n",
                priv->hw->wiphy->fw_version);
 
@@ -1206,8 +1159,16 @@ EXPORT_SYMBOL(iwl_apm_init);
 
 int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
 {
-       int ret = 0;
-       s8 prev_tx_power = priv->tx_power_user_lmt;
+       int ret;
+       s8 prev_tx_power;
+
+       lockdep_assert_held(&priv->mutex);
+
+       if (priv->tx_power_user_lmt == tx_power && !force)
+               return 0;
+
+       if (!priv->cfg->ops->lib->send_tx_power)
+               return -EOPNOTSUPP;
 
        if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) {
                IWL_WARN(priv,
@@ -1224,93 +1185,29 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
                return -EINVAL;
        }
 
-       if (priv->tx_power_user_lmt != tx_power)
-               force = true;
+       if (!iwl_is_ready_rf(priv))
+               return -EIO;
 
-       /* if nic is not up don't send command */
-       if (iwl_is_ready_rf(priv)) {
-               priv->tx_power_user_lmt = tx_power;
-               if (force && priv->cfg->ops->lib->send_tx_power)
-                       ret = priv->cfg->ops->lib->send_tx_power(priv);
-               else if (!priv->cfg->ops->lib->send_tx_power)
-                       ret = -EOPNOTSUPP;
-               /*
-                * if fail to set tx_power, restore the orig. tx power
-                */
-               if (ret)
-                       priv->tx_power_user_lmt = prev_tx_power;
+       /* scan complete use tx_power_next, need to be updated */
+       priv->tx_power_next = tx_power;
+       if (test_bit(STATUS_SCANNING, &priv->status) && !force) {
+               IWL_DEBUG_INFO(priv, "Deferring tx power set while scanning\n");
+               return 0;
        }
 
-       /*
-        * Even this is an async host command, the command
-        * will always report success from uCode
-        * So once driver can placing the command into the queue
-        * successfully, driver can use priv->tx_power_user_lmt
-        * to reflect the current tx power
-        */
-       return ret;
-}
-EXPORT_SYMBOL(iwl_set_tx_power);
+       prev_tx_power = priv->tx_power_user_lmt;
+       priv->tx_power_user_lmt = tx_power;
 
-irqreturn_t iwl_isr_legacy(int irq, void *data)
-{
-       struct iwl_priv *priv = data;
-       u32 inta, inta_mask;
-       u32 inta_fh;
-       unsigned long flags;
-       if (!priv)
-               return IRQ_NONE;
-
-       spin_lock_irqsave(&priv->lock, flags);
+       ret = priv->cfg->ops->lib->send_tx_power(priv);
 
-       /* Disable (but don't clear!) interrupts here to avoid
-        *    back-to-back ISRs and sporadic interrupts from our NIC.
-        * If we have something to service, the tasklet will re-enable ints.
-        * If we *don't* have something, we'll re-enable before leaving here. */
-       inta_mask = iwl_read32(priv, CSR_INT_MASK);  /* just for debug */
-       iwl_write32(priv, CSR_INT_MASK, 0x00000000);
-
-       /* Discover which interrupts are active/pending */
-       inta = iwl_read32(priv, CSR_INT);
-       inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
-
-       /* Ignore interrupt if there's nothing in NIC to service.
-        * This may be due to IRQ shared with another device,
-        * or due to sporadic interrupts thrown from our NIC. */
-       if (!inta && !inta_fh) {
-               IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0, inta_fh == 0\n");
-               goto none;
+       /* if fail to set tx_power, restore the orig. tx power */
+       if (ret) {
+               priv->tx_power_user_lmt = prev_tx_power;
+               priv->tx_power_next = prev_tx_power;
        }
-
-       if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
-               /* Hardware disappeared. It might have already raised
-                * an interrupt */
-               IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
-               goto unplugged;
-       }
-
-       IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
-                     inta, inta_mask, inta_fh);
-
-       inta &= ~CSR_INT_BIT_SCD;
-
-       /* iwl_irq_tasklet() will service interrupts and re-enable them */
-       if (likely(inta || inta_fh))
-               tasklet_schedule(&priv->irq_tasklet);
-
- unplugged:
-       spin_unlock_irqrestore(&priv->lock, flags);
-       return IRQ_HANDLED;
-
- none:
-       /* re-enable interrupts here since we don't have anything to service. */
-       /* only Re-enable if diabled by irq */
-       if (test_bit(STATUS_INT_ENABLED, &priv->status))
-               iwl_enable_interrupts(priv);
-       spin_unlock_irqrestore(&priv->lock, flags);
-       return IRQ_NONE;
+       return ret;
 }
-EXPORT_SYMBOL(iwl_isr_legacy);
+EXPORT_SYMBOL(iwl_set_tx_power);
 
 void iwl_send_bt_config(struct iwl_priv *priv)
 {
@@ -1326,6 +1223,7 @@ void iwl_send_bt_config(struct iwl_priv *priv)
        else
                bt_cmd.flags = BT_COEX_ENABLE;
 
+       priv->bt_enable_flag = bt_cmd.flags;
        IWL_DEBUG_INFO(priv, "BT coex %s\n",
                (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
 
@@ -1452,318 +1350,51 @@ int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL_GPL(iwl_mac_tx_last_beacon);
 
-static void iwl_ht_conf(struct iwl_priv *priv,
-                       struct ieee80211_vif *vif)
+static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
 {
-       struct iwl_ht_config *ht_conf = &priv->current_ht_config;
-       struct ieee80211_sta *sta;
-       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-       struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
-
-       IWL_DEBUG_MAC80211(priv, "enter:\n");
-
-       if (!ctx->ht.enabled)
-               return;
-
-       ctx->ht.protection =
-               bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
-       ctx->ht.non_gf_sta_present =
-               !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
-
-       ht_conf->single_chain_sufficient = false;
-
-       switch (vif->type) {
-       case NL80211_IFTYPE_STATION:
-               rcu_read_lock();
-               sta = ieee80211_find_sta(vif, bss_conf->bssid);
-               if (sta) {
-                       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
-                       int maxstreams;
-
-                       maxstreams = (ht_cap->mcs.tx_params &
-                                     IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
-                                       >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
-                       maxstreams += 1;
-
-                       if ((ht_cap->mcs.rx_mask[1] == 0) &&
-                           (ht_cap->mcs.rx_mask[2] == 0))
-                               ht_conf->single_chain_sufficient = true;
-                       if (maxstreams <= 1)
-                               ht_conf->single_chain_sufficient = true;
-               } else {
-                       /*
-                        * If at all, this can only happen through a race
-                        * when the AP disconnects us while we're still
-                        * setting up the connection, in that case mac80211
-                        * will soon tell us about that.
-                        */
-                       ht_conf->single_chain_sufficient = true;
-               }
-               rcu_read_unlock();
-               break;
-       case NL80211_IFTYPE_ADHOC:
-               ht_conf->single_chain_sufficient = true;
-               break;
-       default:
-               break;
-       }
-
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-}
+       iwl_connection_init_rx_config(priv, ctx);
 
-static inline void iwl_set_no_assoc(struct iwl_priv *priv,
-                                   struct ieee80211_vif *vif)
-{
-       struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+       if (priv->cfg->ops->hcmd->set_rxon_chain)
+               priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
 
-       iwl_led_disassociate(priv);
-       /*
-        * inform the ucode that there is no longer an
-        * association and that no more packets should be
-        * sent
-        */
-       ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-       ctx->staging.assoc_id = 0;
-       iwlcore_commit_rxon(priv, ctx);
+       return iwlcore_commit_rxon(priv, ctx);
 }
 
-static void iwlcore_beacon_update(struct ieee80211_hw *hw,
-                                 struct ieee80211_vif *vif)
+static int iwl_setup_interface(struct iwl_priv *priv,
+                              struct iwl_rxon_context *ctx)
 {
-       struct iwl_priv *priv = hw->priv;
-       unsigned long flags;
-       __le64 timestamp;
-       struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
-
-       if (!skb)
-               return;
-
-       IWL_DEBUG_ASSOC(priv, "enter\n");
+       struct ieee80211_vif *vif = ctx->vif;
+       int err;
 
        lockdep_assert_held(&priv->mutex);
 
-       if (!priv->beacon_ctx) {
-               IWL_ERR(priv, "update beacon but no beacon context!\n");
-               dev_kfree_skb(skb);
-               return;
-       }
-
-       spin_lock_irqsave(&priv->lock, flags);
-
-       if (priv->beacon_skb)
-               dev_kfree_skb(priv->beacon_skb);
-
-       priv->beacon_skb = skb;
-
-       timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
-       priv->timestamp = le64_to_cpu(timestamp);
-
-       IWL_DEBUG_ASSOC(priv, "leave\n");
-
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       if (!iwl_is_ready_rf(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
-               return;
-       }
-
-       priv->cfg->ops->lib->post_associate(priv, priv->beacon_ctx->vif);
-}
-
-void iwl_bss_info_changed(struct ieee80211_hw *hw,
-                         struct ieee80211_vif *vif,
-                         struct ieee80211_bss_conf *bss_conf,
-                         u32 changes)
-{
-       struct iwl_priv *priv = hw->priv;
-       struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
-       int ret;
-
-       IWL_DEBUG_MAC80211(priv, "changes = 0x%X\n", changes);
-
-       if (!iwl_is_alive(priv))
-               return;
-
-       mutex_lock(&priv->mutex);
-
-       if (changes & BSS_CHANGED_QOS) {
-               unsigned long flags;
-
-               spin_lock_irqsave(&priv->lock, flags);
-               ctx->qos_data.qos_active = bss_conf->qos;
-               iwl_update_qos(priv, ctx);
-               spin_unlock_irqrestore(&priv->lock, flags);
-       }
-
-       if (changes & BSS_CHANGED_BEACON_ENABLED) {
-               /*
-                * the add_interface code must make sure we only ever
-                * have a single interface that could be beaconing at
-                * any time.
-                */
-               if (vif->bss_conf.enable_beacon)
-                       priv->beacon_ctx = ctx;
-               else
-                       priv->beacon_ctx = NULL;
-       }
-
-       if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_AP) {
-               dev_kfree_skb(priv->beacon_skb);
-               priv->beacon_skb = ieee80211_beacon_get(hw, vif);
-       }
-
-       if (changes & BSS_CHANGED_BEACON_INT && vif->type == NL80211_IFTYPE_AP)
-               iwl_send_rxon_timing(priv, ctx);
-
-       if (changes & BSS_CHANGED_BSSID) {
-               IWL_DEBUG_MAC80211(priv, "BSSID %pM\n", bss_conf->bssid);
-
-               /*
-                * If there is currently a HW scan going on in the
-                * background then we need to cancel it else the RXON
-                * below/in post_associate will fail.
-                */
-               if (iwl_scan_cancel_timeout(priv, 100)) {
-                       IWL_WARN(priv, "Aborted scan still in progress after 100ms\n");
-                       IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n");
-                       mutex_unlock(&priv->mutex);
-                       return;
-               }
-
-               /* mac80211 only sets assoc when in STATION mode */
-               if (vif->type == NL80211_IFTYPE_ADHOC || bss_conf->assoc) {
-                       memcpy(ctx->staging.bssid_addr,
-                              bss_conf->bssid, ETH_ALEN);
-
-                       /* currently needed in a few places */
-                       memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN);
-               } else {
-                       ctx->staging.filter_flags &=
-                               ~RXON_FILTER_ASSOC_MSK;
-               }
-
-       }
-
        /*
-        * This needs to be after setting the BSSID in case
-        * mac80211 decides to do both changes at once because
-        * it will invoke post_associate.
+        * This variable will be correct only when there's just
+        * a single context, but all code using it is for hardware
+        * that supports only one context.
         */
-       if (vif->type == NL80211_IFTYPE_ADHOC && changes & BSS_CHANGED_BEACON)
-               iwlcore_beacon_update(hw, vif);
-
-       if (changes & BSS_CHANGED_ERP_PREAMBLE) {
-               IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n",
-                                  bss_conf->use_short_preamble);
-               if (bss_conf->use_short_preamble)
-                       ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
-               else
-                       ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK;
-       }
-
-       if (changes & BSS_CHANGED_ERP_CTS_PROT) {
-               IWL_DEBUG_MAC80211(priv, "ERP_CTS %d\n", bss_conf->use_cts_prot);
-               if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ))
-                       ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK;
-               else
-                       ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
-               if (bss_conf->use_cts_prot)
-                       ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
-               else
-                       ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
-       }
-
-       if (changes & BSS_CHANGED_BASIC_RATES) {
-               /* XXX use this information
-                *
-                * To do that, remove code from iwl_set_rate() and put something
-                * like this here:
-                *
-               if (A-band)
-                       ctx->staging.ofdm_basic_rates =
-                               bss_conf->basic_rates;
-               else
-                       ctx->staging.ofdm_basic_rates =
-                               bss_conf->basic_rates >> 4;
-                       ctx->staging.cck_basic_rates =
-                               bss_conf->basic_rates & 0xF;
-                */
-       }
-
-       if (changes & BSS_CHANGED_HT) {
-               iwl_ht_conf(priv, vif);
-
-               if (priv->cfg->ops->hcmd->set_rxon_chain)
-                       priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
-       }
-
-       if (changes & BSS_CHANGED_ASSOC) {
-               IWL_DEBUG_MAC80211(priv, "ASSOC %d\n", bss_conf->assoc);
-               if (bss_conf->assoc) {
-                       priv->timestamp = bss_conf->timestamp;
-
-                       iwl_led_associate(priv);
-
-                       if (!iwl_is_rfkill(priv))
-                               priv->cfg->ops->lib->post_associate(priv, vif);
-               } else
-                       iwl_set_no_assoc(priv, vif);
-       }
-
-       if (changes && iwl_is_associated_ctx(ctx) && bss_conf->aid) {
-               IWL_DEBUG_MAC80211(priv, "Changes (%#x) while associated\n",
-                                  changes);
-               ret = iwl_send_rxon_assoc(priv, ctx);
-               if (!ret) {
-                       /* Sync active_rxon with latest change. */
-                       memcpy((void *)&ctx->active,
-                               &ctx->staging,
-                               sizeof(struct iwl_rxon_cmd));
-               }
-       }
+       priv->iw_mode = vif->type;
 
-       if (changes & BSS_CHANGED_BEACON_ENABLED) {
-               if (vif->bss_conf.enable_beacon) {
-                       memcpy(ctx->staging.bssid_addr,
-                              bss_conf->bssid, ETH_ALEN);
-                       memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN);
-                       iwl_led_associate(priv);
-                       iwlcore_config_ap(priv, vif);
-               } else
-                       iwl_set_no_assoc(priv, vif);
-       }
+       ctx->is_active = true;
 
-       if (changes & BSS_CHANGED_IBSS) {
-               ret = priv->cfg->ops->lib->manage_ibss_station(priv, vif,
-                                                       bss_conf->ibss_joined);
-               if (ret)
-                       IWL_ERR(priv, "failed to %s IBSS station %pM\n",
-                               bss_conf->ibss_joined ? "add" : "remove",
-                               bss_conf->bssid);
+       err = iwl_set_mode(priv, ctx);
+       if (err) {
+               if (!ctx->always_active)
+                       ctx->is_active = false;
+               return err;
        }
 
-       if (changes & BSS_CHANGED_IDLE &&
-           priv->cfg->ops->hcmd->set_pan_params) {
-               if (priv->cfg->ops->hcmd->set_pan_params(priv))
-                       IWL_ERR(priv, "failed to update PAN params\n");
+       if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist &&
+           vif->type == NL80211_IFTYPE_ADHOC) {
+               /*
+                * pretend to have high BT traffic as long as we
+                * are operating in IBSS mode, as this will cause
+                * the rate scaling etc. to behave as intended.
+                */
+               priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
        }
 
-       mutex_unlock(&priv->mutex);
-
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-}
-EXPORT_SYMBOL(iwl_bss_info_changed);
-
-static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif)
-{
-       struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
-
-       iwl_connection_init_rx_config(priv, ctx);
-
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
-
-       return iwlcore_commit_rxon(priv, ctx);
+       return 0;
 }
 
 int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
@@ -1771,7 +1402,7 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        struct iwl_priv *priv = hw->priv;
        struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv;
        struct iwl_rxon_context *tmp, *ctx = NULL;
-       int err = 0;
+       int err;
 
        IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
                           vif->type, vif->addr);
@@ -1813,36 +1444,11 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
        vif_priv->ctx = ctx;
        ctx->vif = vif;
-       /*
-        * This variable will be correct only when there's just
-        * a single context, but all code using it is for hardware
-        * that supports only one context.
-        */
-       priv->iw_mode = vif->type;
-
-       ctx->is_active = true;
 
-       err = iwl_set_mode(priv, vif);
-       if (err) {
-               if (!ctx->always_active)
-                       ctx->is_active = false;
-               goto out_err;
-       }
-
-       if (priv->cfg->bt_params &&
-           priv->cfg->bt_params->advanced_bt_coexist &&
-           vif->type == NL80211_IFTYPE_ADHOC) {
-               /*
-                * pretend to have high BT traffic as long as we
-                * are operating in IBSS mode, as this will cause
-                * the rate scaling etc. to behave as intended.
-                */
-               priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH;
-       }
-
-       goto out;
+       err = iwl_setup_interface(priv, ctx);
+       if (!err)
+               goto out;
 
- out_err:
        ctx->vif = NULL;
        priv->iw_mode = NL80211_IFTYPE_STATION;
  out:
@@ -1853,27 +1459,24 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL(iwl_mac_add_interface);
 
-void iwl_mac_remove_interface(struct ieee80211_hw *hw,
-                             struct ieee80211_vif *vif)
+static void iwl_teardown_interface(struct iwl_priv *priv,
+                                  struct ieee80211_vif *vif,
+                                  bool mode_change)
 {
-       struct iwl_priv *priv = hw->priv;
        struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
 
-       IWL_DEBUG_MAC80211(priv, "enter\n");
-
-       mutex_lock(&priv->mutex);
-
-       WARN_ON(ctx->vif != vif);
-       ctx->vif = NULL;
+       lockdep_assert_held(&priv->mutex);
 
        if (priv->scan_vif == vif) {
                iwl_scan_cancel_timeout(priv, 200);
                iwl_force_scan_end(priv);
        }
-       iwl_set_mode(priv, vif);
 
-       if (!ctx->always_active)
-               ctx->is_active = false;
+       if (!mode_change) {
+               iwl_set_mode(priv, ctx);
+               if (!ctx->always_active)
+                       ctx->is_active = false;
+       }
 
        /*
         * When removing the IBSS interface, overwrite the
@@ -1883,211 +1486,31 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
         * both values are the same and zero.
         */
        if (vif->type == NL80211_IFTYPE_ADHOC)
-               priv->bt_traffic_load = priv->notif_bt_traffic_load;
-
-       memset(priv->bssid, 0, ETH_ALEN);
-       mutex_unlock(&priv->mutex);
-
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-
-}
-EXPORT_SYMBOL(iwl_mac_remove_interface);
-
-/**
- * iwl_mac_config - mac80211 config callback
- */
-int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
-{
-       struct iwl_priv *priv = hw->priv;
-       const struct iwl_channel_info *ch_info;
-       struct ieee80211_conf *conf = &hw->conf;
-       struct ieee80211_channel *channel = conf->channel;
-       struct iwl_ht_config *ht_conf = &priv->current_ht_config;
-       struct iwl_rxon_context *ctx;
-       unsigned long flags = 0;
-       int ret = 0;
-       u16 ch;
-       int scan_active = 0;
-
-       mutex_lock(&priv->mutex);
-
-       IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n",
-                                       channel->hw_value, changed);
-
-       if (unlikely(!priv->cfg->mod_params->disable_hw_scan &&
-                       test_bit(STATUS_SCANNING, &priv->status))) {
-               scan_active = 1;
-               IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
-       }
-
-       if (changed & (IEEE80211_CONF_CHANGE_SMPS |
-                      IEEE80211_CONF_CHANGE_CHANNEL)) {
-               /* mac80211 uses static for non-HT which is what we want */
-               priv->current_ht_config.smps = conf->smps_mode;
-
-               /*
-                * Recalculate chain counts.
-                *
-                * If monitor mode is enabled then mac80211 will
-                * set up the SM PS mode to OFF if an HT channel is
-                * configured.
-                */
-               if (priv->cfg->ops->hcmd->set_rxon_chain)
-                       for_each_context(priv, ctx)
-                               priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx);
-       }
-
-       /* during scanning mac80211 will delay channel setting until
-        * scan finish with changed = 0
-        */
-       if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
-               if (scan_active)
-                       goto set_ch_out;
-
-               ch = channel->hw_value;
-               ch_info = iwl_get_channel_info(priv, channel->band, ch);
-               if (!is_channel_valid(ch_info)) {
-                       IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n");
-                       ret = -EINVAL;
-                       goto set_ch_out;
-               }
-
-               spin_lock_irqsave(&priv->lock, flags);
-
-               for_each_context(priv, ctx) {
-                       /* Configure HT40 channels */
-                       ctx->ht.enabled = conf_is_ht(conf);
-                       if (ctx->ht.enabled) {
-                               if (conf_is_ht40_minus(conf)) {
-                                       ctx->ht.extension_chan_offset =
-                                               IEEE80211_HT_PARAM_CHA_SEC_BELOW;
-                                       ctx->ht.is_40mhz = true;
-                               } else if (conf_is_ht40_plus(conf)) {
-                                       ctx->ht.extension_chan_offset =
-                                               IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
-                                       ctx->ht.is_40mhz = true;
-                               } else {
-                                       ctx->ht.extension_chan_offset =
-                                               IEEE80211_HT_PARAM_CHA_SEC_NONE;
-                                       ctx->ht.is_40mhz = false;
-                               }
-                       } else
-                               ctx->ht.is_40mhz = false;
-
-                       /*
-                        * Default to no protection. Protection mode will
-                        * later be set from BSS config in iwl_ht_conf
-                        */
-                       ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
-
-                       /* if we are switching from ht to 2.4 clear flags
-                        * from any ht related info since 2.4 does not
-                        * support ht */
-                       if ((le16_to_cpu(ctx->staging.channel) != ch))
-                               ctx->staging.flags = 0;
-
-                       iwl_set_rxon_channel(priv, channel, ctx);
-                       iwl_set_rxon_ht(priv, ht_conf);
-
-                       iwl_set_flags_for_band(priv, ctx, channel->band,
-                                              ctx->vif);
-               }
-
-               spin_unlock_irqrestore(&priv->lock, flags);
-
-               if (priv->cfg->ops->lib->update_bcast_stations)
-                       ret = priv->cfg->ops->lib->update_bcast_stations(priv);
-
- set_ch_out:
-               /* The list of supported rates and rate mask can be different
-                * for each band; since the band may have changed, reset
-                * the rate mask to what mac80211 lists */
-               iwl_set_rate(priv);
-       }
-
-       if (changed & (IEEE80211_CONF_CHANGE_PS |
-                       IEEE80211_CONF_CHANGE_IDLE)) {
-               ret = iwl_power_update_mode(priv, false);
-               if (ret)
-                       IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
-       }
-
-       if (changed & IEEE80211_CONF_CHANGE_POWER) {
-               IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n",
-                       priv->tx_power_user_lmt, conf->power_level);
-
-               iwl_set_tx_power(priv, conf->power_level, false);
-       }
-
-       if (!iwl_is_ready(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
-               goto out;
-       }
-
-       if (scan_active)
-               goto out;
-
-       for_each_context(priv, ctx) {
-               if (memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)))
-                       iwlcore_commit_rxon(priv, ctx);
-               else
-                       IWL_DEBUG_INFO(priv,
-                               "Not re-sending same RXON configuration.\n");
-       }
-
-out:
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-       mutex_unlock(&priv->mutex);
-       return ret;
+               priv->bt_traffic_load = priv->last_bt_traffic_load;
 }
-EXPORT_SYMBOL(iwl_mac_config);
 
-void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
+void iwl_mac_remove_interface(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif)
 {
        struct iwl_priv *priv = hw->priv;
-       unsigned long flags;
-       /* IBSS can only be the IWL_RXON_CTX_BSS context */
-       struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
+       struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
 
-       mutex_lock(&priv->mutex);
        IWL_DEBUG_MAC80211(priv, "enter\n");
 
-       spin_lock_irqsave(&priv->lock, flags);
-       memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_config));
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       spin_lock_irqsave(&priv->lock, flags);
-
-       /* new association get rid of ibss beacon skb */
-       if (priv->beacon_skb)
-               dev_kfree_skb(priv->beacon_skb);
-
-       priv->beacon_skb = NULL;
-
-       priv->timestamp = 0;
-
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       iwl_scan_cancel_timeout(priv, 100);
-       if (!iwl_is_ready_rf(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
-               mutex_unlock(&priv->mutex);
-               return;
-       }
+       mutex_lock(&priv->mutex);
 
-       /* we are restarting association process
-        * clear RXON_FILTER_ASSOC_MSK bit
-        */
-       ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-       iwlcore_commit_rxon(priv, ctx);
+       WARN_ON(ctx->vif != vif);
+       ctx->vif = NULL;
 
-       iwl_set_rate(priv);
+       iwl_teardown_interface(priv, vif, false);
 
+       memset(priv->bssid, 0, ETH_ALEN);
        mutex_unlock(&priv->mutex);
 
        IWL_DEBUG_MAC80211(priv, "leave\n");
+
 }
-EXPORT_SYMBOL(iwl_mac_reset_tsf);
+EXPORT_SYMBOL(iwl_mac_remove_interface);
 
 int iwl_alloc_txq_mem(struct iwl_priv *priv)
 {
@@ -2431,77 +1854,115 @@ int iwl_force_reset(struct iwl_priv *priv, int mode, bool external)
        return 0;
 }
 
-/**
- * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
- *
- * During normal condition (no queue is stuck), the timer is continually set to
- * execute every monitor_recover_period milliseconds after the last timer
- * expired.  When the queue read_ptr is at the same place, the timer is
- * shorten to 100mSecs.  This is
- *      1) to reduce the chance that the read_ptr may wrap around (not stuck)
- *      2) to detect the stuck queues quicker before the station and AP can
- *      disassociate each other.
- *
- * This function monitors all the tx queues and recover from it if any
- * of the queues are stuck.
- * 1. It first check the cmd queue for stuck conditions.  If it is stuck,
- *      it will recover by resetting the firmware and return.
- * 2. Then, it checks for station association.  If it associates it will check
- *      other queues.  If any queue is stuck, it will recover by resetting
- *      the firmware.
- * Note: It the number of times the queue read_ptr to be at the same place to
- *      be MAX_REPEAT+1 in order to consider to be stuck.
- */
+int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                            enum nl80211_iftype newtype, bool newp2p)
+{
+       struct iwl_priv *priv = hw->priv;
+       struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+       struct iwl_rxon_context *tmp;
+       u32 interface_modes;
+       int err;
+
+       newtype = ieee80211_iftype_p2p(newtype, newp2p);
+
+       mutex_lock(&priv->mutex);
+
+       interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes;
+
+       if (!(interface_modes & BIT(newtype))) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       if (ctx->exclusive_interface_modes & BIT(newtype)) {
+               for_each_context(priv, tmp) {
+                       if (ctx == tmp)
+                               continue;
+
+                       if (!tmp->vif)
+                               continue;
+
+                       /*
+                        * The current mode switch would be exclusive, but
+                        * another context is active ... refuse the switch.
+                        */
+                       err = -EBUSY;
+                       goto out;
+               }
+       }
+
+       /* success */
+       iwl_teardown_interface(priv, vif, true);
+       vif->type = newtype;
+       err = iwl_setup_interface(priv, ctx);
+       WARN_ON(err);
+       /*
+        * We've switched internally, but submitting to the
+        * device may have failed for some reason. Mask this
+        * error, because otherwise mac80211 will not switch
+        * (and set the interface type back) and we'll be
+        * out of sync with it.
+        */
+       err = 0;
+
+ out:
+       mutex_unlock(&priv->mutex);
+       return err;
+}
+EXPORT_SYMBOL(iwl_mac_change_interface);
+
 /*
- * The maximum number of times the read pointer of the tx queue at the
- * same place without considering to be stuck.
+ * On every watchdog tick we check (latest) time stamp. If it does not
+ * change during timeout period and queue is not empty we reset firmware.
  */
-#define MAX_REPEAT      (2)
 static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
 {
-       struct iwl_tx_queue *txq;
-       struct iwl_queue *q;
+       struct iwl_tx_queue *txq = &priv->txq[cnt];
+       struct iwl_queue *q = &txq->q;
+       unsigned long timeout;
+       int ret;
 
-       txq = &priv->txq[cnt];
-       q = &txq->q;
-       /* queue is empty, skip */
-       if (q->read_ptr == q->write_ptr)
+       if (q->read_ptr == q->write_ptr) {
+               txq->time_stamp = jiffies;
                return 0;
+       }
 
-       if (q->read_ptr == q->last_read_ptr) {
-               /* a queue has not been read from last time */
-               if (q->repeat_same_read_ptr > MAX_REPEAT) {
-                       IWL_ERR(priv,
-                               "queue %d stuck %d time. Fw reload.\n",
-                               q->id, q->repeat_same_read_ptr);
-                       q->repeat_same_read_ptr = 0;
-                       iwl_force_reset(priv, IWL_FW_RESET, false);
-               } else {
-                       q->repeat_same_read_ptr++;
-                       IWL_DEBUG_RADIO(priv,
-                                       "queue %d, not read %d time\n",
-                                       q->id,
-                                       q->repeat_same_read_ptr);
-                       mod_timer(&priv->monitor_recover,
-                               jiffies + msecs_to_jiffies(
-                               IWL_ONE_HUNDRED_MSECS));
-                       return 1;
-               }
-       } else {
-               q->last_read_ptr = q->read_ptr;
-               q->repeat_same_read_ptr = 0;
+       timeout = txq->time_stamp +
+                 msecs_to_jiffies(priv->cfg->base_params->wd_timeout);
+
+       if (time_after(jiffies, timeout)) {
+               IWL_ERR(priv, "Queue %d stuck for %u ms.\n",
+                               q->id, priv->cfg->base_params->wd_timeout);
+               ret = iwl_force_reset(priv, IWL_FW_RESET, false);
+               return (ret == -EAGAIN) ? 0 : 1;
        }
+
        return 0;
 }
 
-void iwl_bg_monitor_recover(unsigned long data)
+/*
+ * Making watchdog tick be a quarter of timeout assure we will
+ * discover the queue hung between timeout and 1.25*timeout
+ */
+#define IWL_WD_TICK(timeout) ((timeout) / 4)
+
+/*
+ * Watchdog timer callback, we check each tx queue for stuck, if if hung
+ * we reset the firmware. If everything is fine just rearm the timer.
+ */
+void iwl_bg_watchdog(unsigned long data)
 {
        struct iwl_priv *priv = (struct iwl_priv *)data;
        int cnt;
+       unsigned long timeout;
 
        if (test_bit(STATUS_EXIT_PENDING, &priv->status))
                return;
 
+       timeout = priv->cfg->base_params->wd_timeout;
+       if (timeout == 0)
+               return;
+
        /* monitor and check for stuck cmd queue */
        if (iwl_check_stuck_queue(priv, priv->cmd_queue))
                return;
@@ -2516,17 +1977,23 @@ void iwl_bg_monitor_recover(unsigned long data)
                                return;
                }
        }
-       if (priv->cfg->base_params->monitor_recover_period) {
-               /*
-                * Reschedule the timer to occur in
-                * priv->cfg->base_params->monitor_recover_period
-                */
-               mod_timer(&priv->monitor_recover, jiffies + msecs_to_jiffies(
-                         priv->cfg->base_params->monitor_recover_period));
-       }
+
+       mod_timer(&priv->watchdog, jiffies +
+                 msecs_to_jiffies(IWL_WD_TICK(timeout)));
 }
-EXPORT_SYMBOL(iwl_bg_monitor_recover);
+EXPORT_SYMBOL(iwl_bg_watchdog);
+
+void iwl_setup_watchdog(struct iwl_priv *priv)
+{
+       unsigned int timeout = priv->cfg->base_params->wd_timeout;
 
+       if (timeout)
+               mod_timer(&priv->watchdog,
+                         jiffies + msecs_to_jiffies(IWL_WD_TICK(timeout)));
+       else
+               del_timer(&priv->watchdog);
+}
+EXPORT_SYMBOL(iwl_setup_watchdog);
 
 /*
  * extended beacon time format
@@ -2584,8 +2051,9 @@ EXPORT_SYMBOL(iwl_add_beacon_time);
 
 #ifdef CONFIG_PM
 
-int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+int iwl_pci_suspend(struct device *device)
 {
+       struct pci_dev *pdev = to_pci_dev(device);
        struct iwl_priv *priv = pci_get_drvdata(pdev);
 
        /*
@@ -2597,18 +2065,14 @@ int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
         */
        iwl_apm_stop(priv);
 
-       pci_save_state(pdev);
-       pci_disable_device(pdev);
-       pci_set_power_state(pdev, PCI_D3hot);
-
        return 0;
 }
 EXPORT_SYMBOL(iwl_pci_suspend);
 
-int iwl_pci_resume(struct pci_dev *pdev)
+int iwl_pci_resume(struct device *device)
 {
+       struct pci_dev *pdev = to_pci_dev(device);
        struct iwl_priv *priv = pci_get_drvdata(pdev);
-       int ret;
        bool hw_rfkill = false;
 
        /*
@@ -2617,11 +2081,6 @@ int iwl_pci_resume(struct pci_dev *pdev)
         */
        pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
 
-       pci_set_power_state(pdev, PCI_D0);
-       ret = pci_enable_device(pdev);
-       if (ret)
-               return ret;
-       pci_restore_state(pdev);
        iwl_enable_interrupts(priv);
 
        if (!(iwl_read32(priv, CSR_GP_CNTRL) &
@@ -2639,4 +2098,14 @@ int iwl_pci_resume(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL(iwl_pci_resume);
 
+const struct dev_pm_ops iwl_pm_ops = {
+       .suspend = iwl_pci_suspend,
+       .resume = iwl_pci_resume,
+       .freeze = iwl_pci_suspend,
+       .thaw = iwl_pci_resume,
+       .poweroff = iwl_pci_suspend,
+       .restore = iwl_pci_resume,
+};
+EXPORT_SYMBOL(iwl_pm_ops);
+
 #endif /* CONFIG_PM */