diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-core.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 971 |
1 files changed, 220 insertions, 751 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 25fb3912342c..efbde1f1a8bf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -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 */ |