From 5691e218a446753a2d21da231651b04e58c76b75 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 31 Dec 2013 21:05:50 +0200 Subject: [PATCH 01/18] iwlwifi: mvm: clear ap_ibss_active in case of failure The state variable was not set to false in case of a failure to complete the start_ap_ibss() flow. Signed-off-by: Ilan Peer Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 14a974e0ca2c..2ac3b25a6d2c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1012,6 +1012,9 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, if (ret) goto out_unbind; + /* must be set before quota calculations */ + mvmvif->ap_ibss_active = true; + ret = iwl_mvm_update_quotas(mvm, vif); if (ret) goto out_rm_bcast; @@ -1026,6 +1029,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, return 0; out_rm_bcast: + mvmvif->ap_ibss_active = false; iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); From a11e144e41f7979608c8bfd35b297b8162fcf32d Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 31 Dec 2013 21:19:55 +0200 Subject: [PATCH 02/18] iwlwifi: mvm: update power after binding in start_ap_ibss() The power settings need to be updated after a binding flow is done and before quota calculations. This was missing in the start_ap_ibss() flow. Fix it. Signed-off-by: Ilan Peer Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 2ac3b25a6d2c..208771a7c74c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1015,9 +1015,13 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* must be set before quota calculations */ mvmvif->ap_ibss_active = true; + /* power updated needs to be done before quotas */ + mvm->bound_vif_cnt++; + iwl_mvm_power_update_binding(mvm, vif, true); + ret = iwl_mvm_update_quotas(mvm, vif); if (ret) - goto out_rm_bcast; + goto out_quota_failed; /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) @@ -1028,7 +1032,9 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); return 0; -out_rm_bcast: +out_quota_failed: + mvm->bound_vif_cnt--; + iwl_mvm_power_update_binding(mvm, vif, false); mvmvif->ap_ibss_active = false; iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); out_unbind: @@ -1061,6 +1067,10 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); + + mvm->bound_vif_cnt--; + iwl_mvm_power_update_binding(mvm, vif, false); + iwl_mvm_mac_ctxt_remove(mvm, vif); mutex_unlock(&mvm->mutex); @@ -1772,11 +1782,11 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, } iwl_mvm_binding_remove_vif(mvm, vif); -out_unlock: - mvmvif->phy_ctxt = NULL; mvm->bound_vif_cnt--; iwl_mvm_power_update_binding(mvm, vif, false); +out_unlock: + mvmvif->phy_ctxt = NULL; mutex_unlock(&mvm->mutex); } From 91b0d1198417cf4fd9a7bd4138b6909f0b359099 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 5 Jan 2014 12:41:12 +0200 Subject: [PATCH 03/18] iwlwifi: mvm: fix missing cleanup in .start() error path Cleanup of iwl_mvm_leds was missing in case of error, resulting in the following warning: WARNING: at lib/kobject.c:196 kobject_add_internal+0x1f4/0x210() kobject_add_internal failed for phy0-led with -EEXIST, don't try to register things with the same name in the same directory. which prevents further reloads of the driver. Cc: stable@vger.kernel.org [3.10+] Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 552c76a926ed..2437a06bf60e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -472,6 +472,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, out_unregister: ieee80211_unregister_hw(mvm->hw); + iwl_mvm_leds_exit(mvm); out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); From 75d3e2d693a695d2a9df27de697fb73cb3f34c23 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 7 Jan 2014 18:19:35 +0200 Subject: [PATCH 04/18] iwlwifi: mvm: rs: fix handling of column switch error If we can't switch to a column because no rates are supported in that column this led to a state where the search cycle got stuck and never ended. This in turn also led to aggregation not being turned on. Fix this by marking a column as visited if we can't switch to it. Reported-and-tested-by: Karl Beldan Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/rs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index c9ff41c514ae..54887b952eb5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1591,6 +1591,8 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, search_tbl->column = col_id; rs_set_expected_tpt_table(lq_sta, search_tbl); + lq_sta->visited_columns |= BIT(col_id); + /* Get the best matching rate if we're changing modes. e.g. * SISO->MIMO, LEGACY->SISO, MIMO->SISO */ @@ -1614,7 +1616,6 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", col_id, rate->index); - lq_sta->visited_columns |= BIT(col_id); return 0; err: From 2775613f4ff32f8be2cae42d542b7fbefcafbfbe Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 2 Jan 2014 10:01:46 +0200 Subject: [PATCH 05/18] iwlwifi: mvm: don't set the drain bit when we flush the AP station When we disassociate in managed mode, we flush the queues after mac80211 has already removed the station. During that time, the pointer to ieee80211_sta to the fw_id_to_mac_id map is -EINVAL. In that case we should not set the station as being drained when the last Tx of this station has exited the shared Tx queue since we are flushing all the queues anyway. The draining logic is meant to be used in GO / AP mode only. In GO / AP mode, we set -EBUSY in the fw_id_to_mac_id map. This is why testing the ieee80211_sta pointer in the fw_id_to_mac_id map with IS_ERR isn't enough to set the station as draining, we need to check that it is -EBUSY. The only impact of the bug was a print: Drained sta 1, but it is internal? Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 3c575a39987b..a4d3bce48fbf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -704,7 +704,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, */ spin_lock_bh(&mvmsta->lock); sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (IS_ERR_OR_NULL(sta)) { + if (!sta || PTR_ERR(sta) == -EBUSY) { /* * Station disappeared in the meantime: * so we are draining. @@ -713,7 +713,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, schedule_work(&mvm->sta_drained_wk); } spin_unlock_bh(&mvmsta->lock); - } else if (!mvmsta) { + } else if (!mvmsta && PTR_ERR(sta) == -EBUSY) { /* Tx response without STA, so we are draining */ set_bit(sta_id, mvm->sta_drained); schedule_work(&mvm->sta_drained_wk); From 2d93aee152b1758a94a18fe15d72153ba73b5679 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Dec 2013 14:15:41 +0200 Subject: [PATCH 06/18] iwlwifi: pcie: enable oscillator for L1 exit Enabling the oscillator consumes slightly more power (100uA) but allows to make sure that we exit from L1 on time. Not doing so might lead to a PCIe specification violation since we might wake up from L1 at the wrong time. This issue has been identified on 3160 and 7260 only. On older NICs L1 off is not enabled, on newer NICs (7265), the issue is fixed. When the bug occurs the user sees that the NIC has disappeared from the PCI bridge, any access to the device returns 0xff. This fixes: https://bugzilla.kernel.org/show_bug.cgi?id=64541 and has been extensively discussed here: http://markmail.org/thread/mfmpzqt3r333n4bo Cc: stable@vger.kernel.org [3.10+] Fixes: 99cd47142399 ("iwlwifi: add 7000 series device configuration") Reported-and-tested-by: wzyboy Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-prph.h | 4 ++++ drivers/net/wireless/iwlwifi/pcie/trans.c | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index d69b0fb0a434..100bd0d79681 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -277,4 +277,8 @@ static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) /*********************** END TX SCHEDULER *************************************/ +/* Oscillator clock */ +#define OSC_CLK (0xa04068) +#define OSC_CLK_FORCE_CONTROL (0x8) + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 16f66c1a23de..f9507807b486 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -178,6 +178,28 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) goto out; } + if (trans->cfg->host_interrupt_operation_mode) { + /* + * This is a bit of an abuse - This is needed for 7260 / 3160 + * only check host_interrupt_operation_mode even if this is + * not related to host_interrupt_operation_mode. + * + * Enable the oscillator to count wake up time for L1 exit. This + * consumes slightly more power (100uA) - but allows to be sure + * that we wake up from L1 on time. + * + * This looks weird: read twice the same register, discard the + * value, set a bit, and yet again, read that same register + * just to discard the value. But that's the way the hardware + * seems to like it. + */ + iwl_read_prph(trans, OSC_CLK); + iwl_read_prph(trans, OSC_CLK); + iwl_set_bits_prph(trans, OSC_CLK, OSC_CLK_FORCE_CONTROL); + iwl_read_prph(trans, OSC_CLK); + iwl_read_prph(trans, OSC_CLK); + } + /* * Enable DMA clock and wait for it to stabilize. * From cf9d11888091920e356d085a5fa1644fc5c05429 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Dec 2013 18:54:06 +0200 Subject: [PATCH 07/18] iwlwifi: mvm: fix tx seq_ctrl debug print Since seq_number is incremented right after using it, so printed seq_ctrl was actually the next one to be used. Fix it by incrementing the seq_number only later, before saving it. Additionally, use the IEEE80211_SEQ_TO_SN macro in order to print the actual sequence number. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/tx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index a4d3bce48fbf..90378c217bc7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -390,7 +390,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; is_data_qos = true; is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; } @@ -407,13 +406,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, } IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, - tid, txq_id, seq_number); + tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta; if (is_data_qos && !ieee80211_has_morefrags(fc)) - mvmsta->tid_data[tid].seq_number = seq_number; + mvmsta->tid_data[tid].seq_number = seq_number + 0x10; spin_unlock(&mvmsta->lock); From 099d8f20b65d0dac07fb6e2ca0ed19e5d36d7d2e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 5 Jan 2014 15:09:44 +0200 Subject: [PATCH 08/18] iwlwifi: mvm: send all the NVM sections to the NIC Some NIC comes with more than the 4 NVM (non volative memory) sections described in the nvm_to_read array. These NICs usually get their NVM from an external file fetched from userland during init. We already parsed the file, but sent to the NIC only 4 NVM sections whereas there could be more sections in the file. Fix this. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index c6beb0f842d5..9affd43db811 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -393,16 +393,15 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) { int i, ret; - u16 section_id; struct iwl_nvm_section *sections = mvm->nvm_sections; IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); - for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { - section_id = nvm_to_read[i]; - ret = iwl_nvm_write_section(mvm, section_id, - sections[section_id].data, - sections[section_id].length); + for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) { + if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length) + continue; + ret = iwl_nvm_write_section(mvm, i, sections[i].data, + sections[i].length); if (ret < 0) { IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); break; From 560843f4ab7ba8c2e5edf01949bba4d7a94f01e9 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sun, 5 Jan 2014 21:04:19 +0200 Subject: [PATCH 09/18] iwlwifi: mvm: rs: fix a theoretical out of bounds access Discovered by klocwork Array 'iwl_rate_mcs' of size 15 may use index value(s) -1 * rs.c:2562: index = iwl_hwrate_to_plcp_idx(rate) * rs.c:2562: Result of function call 'iwl_hwrate_to_plcp_idx(rate)' is '[-1,14]' * rs.c:2565: Array 'iwl_rate_mcs' size is 15. * rs.c:2565: Possible attempt to access element -1 of array 'iwl_rate_mcs'. While at it stop using index = -1 and always use IWL_RATE_INVALID Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/rs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 54887b952eb5..c4f214d4c44e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -357,7 +357,7 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) return idx; } - return -1; + return IWL_RATE_INVALID; } static void rs_rate_scale_perform(struct iwl_mvm *mvm, @@ -703,10 +703,8 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, memset(rate, 0, sizeof(*rate)); rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); - if (rate->index == IWL_RATE_INVALID) { - rate->index = -1; + if (rate->index == IWL_RATE_INVALID) return -EINVAL; - } rate->ant = (ant_msk >> RATE_MCS_ANT_POS); @@ -2562,7 +2560,9 @@ static int rs_pretty_print_rate(char *buf, const u32 rate) int index = iwl_hwrate_to_plcp_idx(rate); return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n", - rs_pretty_ant(ant), iwl_rate_mcs[index].mbps); + rs_pretty_ant(ant), + index == IWL_RATE_INVALID ? "BAD" : + iwl_rate_mcs[index].mbps); } if (rate & RATE_MCS_VHT_MSK) { From 01e0efe3178dbeed1284e55b70a3160a279995b2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 6 Jan 2014 09:05:54 +0200 Subject: [PATCH 10/18] iwlwifi: mvm: fix SRAM dump debugfs handler If the length isn't set it means we want all the SRAM. Also - this is perfectly valid to partially dump starting at offset 0. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 76cdce9edf55..369d4c90e669 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -135,7 +135,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; len = img->sec[IWL_UCODE_SECTION_DATA].len; - if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) { + if (mvm->dbgfs_sram_len) { ofs = mvm->dbgfs_sram_offset; len = mvm->dbgfs_sram_len; } From 9ddca8607fc1850376a018b3be0a437f0bf99a2f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Jan 2014 09:29:40 +0100 Subject: [PATCH 11/18] iwlwifi: mvm: use array indexing instead of treating it as a pointer It's a bit strange to treat an array as a pointer, so use proper array indexing instead. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 208771a7c74c..797b4d91cfd7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -262,9 +262,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; /* currently FW API supports only one optional cipher scheme */ - if (mvm->fw->cs->cipher) { + if (mvm->fw->cs[0].cipher) { mvm->hw->n_cipher_schemes = 1; - mvm->hw->cipher_schemes = mvm->fw->cs; + mvm->hw->cipher_schemes = &mvm->fw->cs[0]; } #ifdef CONFIG_PM_SLEEP From 4af8beaa7b32ca3e7479bf0189f973ac40c1cff6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 1 Jan 2014 10:05:55 +0200 Subject: [PATCH 12/18] iwlwifi: mvm: remove unused flags from add station command Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 6bbbad453a3b..1b60fdff6a56 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -97,9 +97,6 @@ enum iwl_sta_flags { STA_FLG_FLG_ANT_B), STA_FLG_PS = BIT(8), - STA_FLG_INVALID = BIT(9), - STA_FLG_DLP_EN = BIT(10), - STA_FLG_SET_ALL_KEYS = BIT(11), STA_FLG_DRAIN_FLOW = BIT(12), STA_FLG_PAN = BIT(13), STA_FLG_CLASS_AUTH = BIT(14), From f94045ed19821cee923f79953f3f35a2de81f695 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 6 Jan 2014 13:38:55 +0200 Subject: [PATCH 13/18] iwlwifi: mvm: reset Thermal Throttling's SMPS request upon disassociation The request of SMPS issued by the Thermal Throttling code was not reset when we disassociated - fix that. Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 797b4d91cfd7..e843080b5c0b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -944,6 +944,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_ERR(mvm, "failed to update power mode\n"); } iwl_mvm_bt_coex_vif_change(mvm); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, + IEEE80211_SMPS_AUTOMATIC); } else if (changes & BSS_CHANGED_BEACON_INFO) { /* * We received a beacon _after_ association so From e36b766d0c674c9dc73a2ace7aa37aea78afa233 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Mon, 6 Jan 2014 20:16:48 +0200 Subject: [PATCH 14/18] iwlwifi: change beamformee STS cap All beamformee supporting chips have the ability to support VHT NDP in up to 4 STSs. So change the published beamformee STS cap accordingly to 3 as it should be Nsts-1. Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 1b61cb529948..0c677b4e68cb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -264,14 +264,13 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, struct ieee80211_sta_vht_cap *vht_cap) { int num_ants = num_of_ant(data->valid_rx_ant); - int bf_sts_cap = num_ants - 1; vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | IEEE80211_VHT_CAP_RXSTBC_1 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | - bf_sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | + 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; if (num_ants > 1) From 65b30348dbf9b529901b5c2b62dca6cad9017a2a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Jan 2014 13:16:33 +0100 Subject: [PATCH 15/18] iwlwifi: add inline helper for packet lengths Add an inline helper function for getting an RX packet's length or payload length and use it throughout the code (most of which I did using an spatch.) While at it, adjust some code, and remove a bogus comment from the dvm calibration code. Signed-off-by: Johannes Berg Reviewed-by: Eran Harary Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 3 +-- drivers/net/wireless/iwlwifi/dvm/rx.c | 7 ++----- drivers/net/wireless/iwlwifi/dvm/ucode.c | 7 +------ drivers/net/wireless/iwlwifi/iwl-trans.h | 10 ++++++++++ drivers/net/wireless/iwlwifi/mvm/d3.c | 15 ++++++--------- drivers/net/wireless/iwlwifi/mvm/time-event.c | 4 ++-- drivers/net/wireless/iwlwifi/mvm/utils.c | 4 ++-- drivers/net/wireless/iwlwifi/pcie/rx.c | 2 +- 8 files changed, 25 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 8a3286a2bb31..e2ba4a725b34 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -407,9 +407,8 @@ static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait, { struct iwl_resume_data *resume_data = data; struct iwl_priv *priv = resume_data->priv; - u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - 4 != sizeof(*resume_data->cmd)) { + if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) { IWL_ERR(priv, "rx wrong size data\n"); return true; } diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index b68bb2f4d2c2..7a1bc1c547e1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -205,8 +205,7 @@ static int iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 __maybe_unused len = - le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + u32 __maybe_unused len = iwl_rx_packet_len(pkt); IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len); iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len); @@ -457,7 +456,7 @@ static int iwlagn_rx_statistics(struct iwl_priv *priv, const int reg_recalib_period = 60; int change; struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + u32 len = iwl_rx_packet_payload_len(pkt); __le32 *flag; struct statistics_general_common *common; struct statistics_rx_non_phy *rx_non_phy; @@ -467,8 +466,6 @@ static int iwlagn_rx_statistics(struct iwl_priv *priv, struct statistics_tx *tx; struct statistics_bt_activity *bt_activity; - len -= sizeof(struct iwl_cmd_header); /* skip header */ - IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", len); diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 4da6d90b0829..998063580705 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -389,7 +389,6 @@ static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, { struct iwl_priv *priv = data; struct iwl_calib_hdr *hdr; - int len; if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); @@ -397,12 +396,8 @@ static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, } hdr = (struct iwl_calib_hdr *)pkt->data; - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - /* reduce the size by the length field itself */ - len -= sizeof(__le32); - - if (iwl_calib_set(priv, hdr, len)) + if (iwl_calib_set(priv, hdr, iwl_rx_packet_payload_len(pkt))) IWL_ERR(priv, "Failed to record calibration data %d\n", hdr->op_code); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 8d1b5ed3502a..1f065cf4a4ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -176,6 +176,16 @@ struct iwl_rx_packet { u8 data[]; } __packed; +static inline u32 iwl_rx_packet_len(const struct iwl_rx_packet *pkt) +{ + return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; +} + +static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) +{ + return iwl_rx_packet_len(pkt) - sizeof(pkt->hdr); +} + /** * enum CMD_MODE - how to send the host commands ? * diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index f04d2f4d80cd..f36a7ee0267f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -886,8 +886,7 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, if (err) return err; - size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - size -= sizeof(cmd.resp_pkt->hdr); + size = iwl_rx_packet_payload_len(cmd.resp_pkt); if (size < sizeof(__le16)) { err = -EINVAL; } else { @@ -1211,9 +1210,8 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; #ifdef CONFIG_IWLWIFI_DEBUGFS - len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) & - FH_RSCSR_FRAME_SIZE_MSK; - if (len >= sizeof(u32) * 2) { + len = iwl_rx_packet_payload_len(d3_cfg_cmd.resp_pkt); + if (len >= sizeof(u32)) { mvm->d3_test_pme_ptr = le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data); } @@ -1668,8 +1666,8 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, else status_size = sizeof(struct iwl_wowlan_status_v4); - len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - sizeof(struct iwl_cmd_header) < status_size) { + len = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (len < status_size) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } @@ -1704,8 +1702,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, status.wake_packet = status_v4->wake_packet; } - if (len - sizeof(struct iwl_cmd_header) != - status_size + ALIGN(status.wake_packet_bufsize, 4)) { + if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 50f3d7f560bc..b4c2abaa297b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -249,12 +249,12 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, container_of(notif_wait, struct iwl_mvm, notif_wait); struct iwl_mvm_time_event_data *te_data = data; struct iwl_time_event_resp *resp; - int resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + int resp_len = iwl_rx_packet_payload_len(pkt); if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) return true; - if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); return true; } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 487d61b25359..a4a5e25623c3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -168,8 +168,8 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, goto out_free_resp; } - resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { + resp_len = iwl_rx_packet_payload_len(pkt); + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { ret = -EIO; goto out_free_resp; } diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 1890ea29c264..08c23d497a02 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -615,7 +615,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, rxcb._offset, get_cmd_string(trans_pcie, pkt->hdr.cmd), pkt->hdr.cmd); - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + len = iwl_rx_packet_len(pkt); len += sizeof(u32); /* account for status word */ trace_iwlwifi_dev_rx(trans->dev, trans, pkt, len); trace_iwlwifi_dev_rx_data(trans->dev, trans, pkt, len); From 05159fccf21cde603f7be8815a94ac85e9c0f245 Mon Sep 17 00:00:00 2001 From: Eytan Lifshitz Date: Tue, 7 Jan 2014 12:50:45 +0200 Subject: [PATCH 16/18] iwlwifi: mvm: fix theoretical uninitialized function return value If we try to write NVM that do not exist, the function will return uninitialized value. fixed. Signed-off-by: Eytan Lifshitz Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 9affd43db811..35b71af78d02 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -392,7 +392,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) /* Loads the NVM data stored in mvm->nvm_sections into the NIC */ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) { - int i, ret; + int i, ret = 0; struct iwl_nvm_section *sections = mvm->nvm_sections; IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); From 7a06a38a115c4d8c1b442e544047fb151d5d4e03 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 9 Jan 2014 09:55:38 +0200 Subject: [PATCH 17/18] iwlwifi: mvm: add REPLY_SF_CFG_CMD to cmd strings Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 2437a06bf60e..a3d43de342d7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -309,6 +309,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(BT_PROFILE_NOTIFICATION), CMD(BT_CONFIG), CMD(MCAST_FILTER_CMD), + CMD(REPLY_SF_CFG_CMD), CMD(REPLY_BEACON_FILTERING_CMD), CMD(REPLY_THERMAL_MNG_BACKOFF), CMD(MAC_PM_POWER_TABLE), From cf38e4f756d6396657c76f72ef42b6f47c523f97 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sun, 5 Jan 2014 19:27:24 +0200 Subject: [PATCH 18/18] iwlwifi: mvm: don't use highest rate in VHT MCS Set Keeping this as 0 is ok according to spec section 9.7.11 as this means the limits are according to the Tx/Rx supported MCS x NSS bitmap. Initially we've set these as there were concerns of interop issues but these turned out to be false. Signed-off-by: Eyal Shapira Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 0c677b4e68cb..f06f4cbe1317 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -289,9 +289,6 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); - /* Max rate for Long GI NSS=2 80Mhz is 780Mbps */ - vht_cap->vht_mcs.rx_highest = cpu_to_le16(780); - if (num_ants == 1 || cfg->rx_with_siso_diversity) { vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | @@ -299,12 +296,9 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, /* this works because NOT_SUPPORTED == 3 */ vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); - /* Max rate for Long GI NSS=1 80Mhz is 390Mbps */ - vht_cap->vht_mcs.rx_highest = cpu_to_le16(390); } vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; - vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest; } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,