| /****************************************************************************** |
| * |
| * GPL LICENSE SUMMARY |
| * |
| * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, |
| * USA |
| * |
| * The full GNU General Public License is included in this distribution |
| * in the file called LICENSE.GPL. |
| * |
| * Contact Information: |
| * Intel Linux Wireless <ilw@linux.intel.com> |
| * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
| *****************************************************************************/ |
| |
| #include <linux/kernel.h> |
| #include <net/mac80211.h> |
| |
| #include "iwl-dev.h" |
| #include "iwl-core.h" |
| #include "iwl-helpers.h" |
| #include "iwl-legacy.h" |
| |
| 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); |
| } |
| |
| /** |
| * iwl_legacy_mac_config - mac80211 config callback |
| */ |
| int iwl_legacy_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; |
| bool ht_changed[NUM_IWL_RXON_CTX] = {}; |
| |
| if (WARN_ON(!priv->cfg->ops->legacy)) |
| return -EOPNOTSUPP; |
| |
| 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 */ |
| if (ctx->ht.enabled != conf_is_ht(conf)) { |
| ctx->ht.enabled = conf_is_ht(conf); |
| ht_changed[ctx->ctxid] = true; |
| } |
| 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->legacy->update_bcast_stations) |
| ret = priv->cfg->ops->legacy->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"); |
| if (ht_changed[ctx->ctxid]) |
| iwl_update_qos(priv, ctx); |
| } |
| |
| out: |
| IWL_DEBUG_MAC80211(priv, "leave\n"); |
| mutex_unlock(&priv->mutex); |
| return ret; |
| } |
| EXPORT_SYMBOL(iwl_legacy_mac_config); |
| |
| void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw) |
| { |
| 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]; |
| |
| if (WARN_ON(!priv->cfg->ops->legacy)) |
| return; |
| |
| 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; |
| } |
| |
| /* we are restarting association process |
| * clear RXON_FILTER_ASSOC_MSK bit |
| */ |
| ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; |
| iwlcore_commit_rxon(priv, ctx); |
| |
| iwl_set_rate(priv); |
| |
| mutex_unlock(&priv->mutex); |
| |
| IWL_DEBUG_MAC80211(priv, "leave\n"); |
| } |
| EXPORT_SYMBOL(iwl_legacy_mac_reset_tsf); |
| |
| static void iwl_ht_conf(struct iwl_priv *priv, |
| struct ieee80211_vif *vif) |
| { |
| 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_ASSOC(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_ASSOC(priv, "leave\n"); |
| } |
| |
| 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); |
| |
| 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); |
| } |
| |
| static void iwlcore_beacon_update(struct ieee80211_hw *hw, |
| struct ieee80211_vif *vif) |
| { |
| 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_MAC80211(priv, "enter\n"); |
| |
| 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_MAC80211(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->legacy->post_associate(priv); |
| } |
| |
| void iwl_legacy_mac_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; |
| |
| if (WARN_ON(!priv->cfg->ops->legacy)) |
| return; |
| |
| 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. |
| */ |
| 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->legacy->post_associate(priv); |
| } 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)); |
| } |
| } |
| |
| 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); |
| priv->cfg->ops->legacy->config_ap(priv); |
| } else |
| iwl_set_no_assoc(priv, vif); |
| } |
| |
| if (changes & BSS_CHANGED_IBSS) { |
| ret = priv->cfg->ops->legacy->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); |
| } |
| |
| mutex_unlock(&priv->mutex); |
| |
| IWL_DEBUG_MAC80211(priv, "leave\n"); |
| } |
| EXPORT_SYMBOL(iwl_legacy_mac_bss_info_changed); |
| |
| 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); |
| |
| /* 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 ((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 disabled by irq */ |
| if (test_bit(STATUS_INT_ENABLED, &priv->status)) |
| iwl_enable_interrupts(priv); |
| spin_unlock_irqrestore(&priv->lock, flags); |
| return IRQ_NONE; |
| } |
| EXPORT_SYMBOL(iwl_isr_legacy); |
| |
| /* |
| * iwl_legacy_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this |
| * function. |
| */ |
| void iwl_legacy_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(iwl_legacy_tx_cmd_protection); |