Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi...
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 18 Dec 2013 20:09:58 +0000 (15:09 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 18 Dec 2013 20:09:58 +0000 (15:09 -0500)
35 files changed:
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/binding.c
drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/sf.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c

index 217f1ca..9f4239d 100644 (file)
@@ -322,12 +322,6 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw)
 
        flush_workqueue(priv->workqueue);
 
-       /* User space software may expect getting rfkill changes
-        * even if interface is down, trans->down will leave the RF
-        * kill interrupt enabled
-        */
-       iwl_trans_stop_hw(priv->trans, false);
-
        IWL_DEBUG_MAC80211(priv, "leave\n");
 }
 
index 7aad766..fd9f6cf 100644 (file)
@@ -1313,7 +1313,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
        }
 
        /* Reset chip to save power until we load uCode during "up". */
-       iwl_trans_stop_hw(priv->trans, false);
+       iwl_trans_stop_device(priv->trans);
 
        priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg,
                                                  priv->eeprom_blob,
@@ -1458,7 +1458,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
 
        dev_kfree_skb(priv->beacon_skb);
 
-       iwl_trans_stop_hw(priv->trans, true);
+       iwl_trans_op_mode_leave(priv->trans);
        ieee80211_free_hw(priv->hw);
 }
 
index 3c34a72..5fb3772 100644 (file)
@@ -108,7 +108,7 @@ static const struct iwl_base_params iwl7000_base_params = {
 };
 
 static const struct iwl_ht_params iwl7000_ht_params = {
-       .use_rts_for_aggregation = true, /* use rts/cts protection */
+       .stbc = true,
        .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
 };
 
index 0b91624..e05440f 100644 (file)
@@ -162,12 +162,14 @@ struct iwl_base_params {
 };
 
 /*
+ * @stbc: support Tx STBC and 1*SS Rx STBC
  * @use_rts_for_aggregation: use rts/cts protection for HT traffic
  * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40
  */
 struct iwl_ht_params {
        enum ieee80211_smps_mode smps_mode;
        const bool ht_greenfield_support; /* if used set to true */
+       const bool stbc;
        bool use_rts_for_aggregation;
        u8 ht40_bands;
 };
index ff57002..4bebfb5 100644 (file)
@@ -322,6 +322,41 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces,
        pieces->img[type].sec[sec].offset = offset;
 }
 
+static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len)
+{
+       int i, j;
+       struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data;
+       struct iwl_fw_cipher_scheme *fwcs;
+       struct ieee80211_cipher_scheme *cs;
+       u32 cipher;
+
+       if (len < sizeof(*l) ||
+           len < sizeof(l->size) + l->size * sizeof(l->cs[0]))
+               return -EINVAL;
+
+       for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) {
+               fwcs = &l->cs[j];
+               cipher = le32_to_cpu(fwcs->cipher);
+
+               /* we skip schemes with zero cipher suite selector */
+               if (!cipher)
+                       continue;
+
+               cs = &fw->cs[j++];
+               cs->cipher = cipher;
+               cs->iftype = BIT(NL80211_IFTYPE_STATION);
+               cs->hdr_len = fwcs->hdr_len;
+               cs->pn_len = fwcs->pn_len;
+               cs->pn_off = fwcs->pn_off;
+               cs->key_idx_off = fwcs->key_idx_off;
+               cs->key_idx_mask = fwcs->key_idx_mask;
+               cs->key_idx_shift = fwcs->key_idx_shift;
+               cs->mic_len = fwcs->mic_len;
+       }
+
+       return 0;
+}
+
 /*
  * Gets uCode section from tlv.
  */
@@ -729,6 +764,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                return -EINVAL;
                        }
                        break;
+               case IWL_UCODE_TLV_CSCHEME:
+                       if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
+                               goto invalid_tlv_len;
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
index f4a6d31..4380c16 100644 (file)
@@ -751,6 +751,13 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
        ht_info->ht_supported = true;
        ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
 
+       if (cfg->ht_params->stbc) {
+               ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+
+               if (tx_chains > 1)
+                       ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+       }
+
        if (iwlwifi_mod_params.amsdu_size_8K)
                ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
 
index 6c6c35c..4f95734 100644 (file)
@@ -125,6 +125,7 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_SECURE_SEC_INIT   = 25,
        IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
        IWL_UCODE_TLV_NUM_OF_CPU        = 27,
+       IWL_UCODE_TLV_CSCHEME           = 28,
 };
 
 struct iwl_ucode_tlv {
index 75db087..8704e30 100644 (file)
@@ -92,6 +92,9 @@
  * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
  * @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command
  *     containing CAM (Continuous Active Mode) indication.
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a
+ *     single bound interface).
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
  */
 enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
@@ -113,7 +116,9 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_SCHED_SCAN          = BIT(17),
        IWL_UCODE_TLV_FLAGS_STA_KEY_CMD         = BIT(19),
        IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD       = BIT(20),
+       IWL_UCODE_TLV_FLAGS_P2P_PS              = BIT(21),
        IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
+       IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD        = BIT(26),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -209,6 +214,44 @@ enum iwl_fw_phy_cfg {
        FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
 };
 
+#define IWL_UCODE_MAX_CS               1
+
+/**
+ * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
+ * @cipher: a cipher suite selector
+ * @flags: cipher scheme flags (currently reserved for a future use)
+ * @hdr_len: a size of MPDU security header
+ * @pn_len: a size of PN
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: bit shift needed to get key_idx
+ * @mic_len: mic length in bytes
+ * @hw_cipher: a HW cipher index used in host commands
+ */
+struct iwl_fw_cipher_scheme {
+       __le32 cipher;
+       u8 flags;
+       u8 hdr_len;
+       u8 pn_len;
+       u8 pn_off;
+       u8 key_idx_off;
+       u8 key_idx_mask;
+       u8 key_idx_shift;
+       u8 mic_len;
+       u8 hw_cipher;
+} __packed;
+
+/**
+ * struct iwl_fw_cscheme_list - a cipher scheme list
+ * @size: a number of entries
+ * @cs: cipher scheme entries
+ */
+struct iwl_fw_cscheme_list {
+       u8 size;
+       struct iwl_fw_cipher_scheme cs[];
+} __packed;
+
 /**
  * struct iwl_fw - variables associated with the firmware
  *
@@ -224,6 +267,7 @@ enum iwl_fw_phy_cfg {
  * @inst_evtlog_size: event log size for runtime ucode.
  * @inst_errlog_ptr: error log offfset for runtime ucode.
  * @mvm_fw: indicates this is MVM firmware
+ * @cipher_scheme: optional external cipher scheme.
  */
 struct iwl_fw {
        u32 ucode_ver;
@@ -243,6 +287,8 @@ struct iwl_fw {
        u32 phy_config;
 
        bool mvm_fw;
+
+       struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
 };
 
 static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw)
index 94aef22..a48decc 100644 (file)
@@ -263,13 +263,20 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
                                  struct iwl_nvm_data *data,
                                  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 |
                       7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
 
+       if (num_ants > 1)
+               vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
+
        if (iwlwifi_mod_params.amsdu_size_8K)
                vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
 
@@ -283,16 +290,22 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
                            IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
                            IEEE80211_VHT_MCS_NOT_SUPPORTED << 14);
 
-       if (num_of_ant(data->valid_rx_ant) == 1 ||
+       /* 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 |
                                IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
                /* 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,
index 976448a..f50e6c6 100644 (file)
@@ -155,14 +155,12 @@ void iwl_opmode_deregister(const char *name);
 
 /**
  * struct iwl_op_mode - operational mode
+ * @ops - pointer to its own ops
  *
  * This holds an implementation of the mac80211 / fw API.
- *
- * @ops - pointer to its own ops
  */
 struct iwl_op_mode {
        const struct iwl_op_mode_ops *ops;
-       const struct iwl_trans *trans;
 
        char op_mode_specific[0] __aligned(sizeof(void *));
 };
index 143292b..0c36478 100644 (file)
@@ -70,6 +70,7 @@
 #include "iwl-debug.h"
 #include "iwl-config.h"
 #include "iwl-fw.h"
+#include "iwl-op-mode.h"
 
 /**
  * DOC: Transport layer - what is it ?
  *        start_fw
  *
  *     5) Then when finished (or reset):
- *        stop_fw (a.k.a. stop device for the moment)
- *        stop_hw
+ *        stop_device
  *
  *     6) Eventually, the free function will be called.
  */
@@ -318,6 +318,24 @@ enum iwl_d3_status {
 };
 
 /**
+ * enum iwl_trans_status: transport status flags
+ * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed
+ * @STATUS_DEVICE_ENABLED: APM is enabled
+ * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
+ * @STATUS_INT_ENABLED: interrupts are enabled
+ * @STATUS_RFKILL: the HW RFkill switch is in KILL position
+ * @STATUS_FW_ERROR: the fw is in error state
+ */
+enum iwl_trans_status {
+       STATUS_SYNC_HCMD_ACTIVE,
+       STATUS_DEVICE_ENABLED,
+       STATUS_TPOWER_PMI,
+       STATUS_INT_ENABLED,
+       STATUS_RFKILL,
+       STATUS_FW_ERROR,
+};
+
+/**
  * struct iwl_trans_config - transport configuration
  *
  * @op_mode: pointer to the upper layer.
@@ -361,9 +379,7 @@ struct iwl_trans;
  *
  * @start_hw: starts the HW- from that point on, the HW can send interrupts
  *     May sleep
- * @stop_hw: stops the HW- from that point on, the HW will be in low power but
- *     will still issue interrupt if the HW RF kill is triggered unless
- *     op_mode_leaving is true.
+ * @op_mode_leave: Turn off the HW RF kill indication if on
  *     May sleep
  * @start_fw: allocates and inits all the resources for the transport
  *     layer. Also kick a fw image.
@@ -371,8 +387,11 @@ struct iwl_trans;
  * @fw_alive: called when the fw sends alive notification. If the fw provides
  *     the SCD base address in SRAM, then provide it here, or 0 otherwise.
  *     May sleep
- * @stop_device:stops the whole device (embedded CPU put to reset)
- *     May sleep
+ * @stop_device: stops the whole device (embedded CPU put to reset) and stops
+ *     the HW. From that point on, the HW will be in low power but will still
+ *     issue interrupt if the HW RF kill is triggered. This callback must do
+ *     the right thing and not crash even if start_hw() was called but not
+ *     start_fw(). May sleep
  * @d3_suspend: put the device into the correct mode for WoWLAN during
  *     suspend. This is optional, if not implemented WoWLAN will not be
  *     supported. This callback may sleep.
@@ -418,7 +437,7 @@ struct iwl_trans;
 struct iwl_trans_ops {
 
        int (*start_hw)(struct iwl_trans *iwl_trans);
-       void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving);
+       void (*op_mode_leave)(struct iwl_trans *iwl_trans);
        int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
                        bool run_in_rfkill);
        void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
@@ -479,6 +498,7 @@ enum iwl_trans_state {
  * @ops - pointer to iwl_trans_ops
  * @op_mode - pointer to the op_mode
  * @cfg - pointer to the configuration
+ * @status: a bit-mask of transport status flags
  * @dev - pointer to struct device * that represents the device
  * @hw_id: a u32 with the ID of the device / subdevice.
  *     Set during transport allocation.
@@ -499,6 +519,7 @@ struct iwl_trans {
        struct iwl_op_mode *op_mode;
        const struct iwl_cfg *cfg;
        enum iwl_trans_state state;
+       unsigned long status;
 
        struct device *dev;
        u32 hw_rev;
@@ -540,15 +561,14 @@ static inline int iwl_trans_start_hw(struct iwl_trans *trans)
        return trans->ops->start_hw(trans);
 }
 
-static inline void iwl_trans_stop_hw(struct iwl_trans *trans,
-                                    bool op_mode_leaving)
+static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans)
 {
        might_sleep();
 
-       trans->ops->stop_hw(trans, op_mode_leaving);
+       if (trans->ops->op_mode_leave)
+               trans->ops->op_mode_leave(trans);
 
-       if (op_mode_leaving)
-               trans->op_mode = NULL;
+       trans->op_mode = NULL;
 
        trans->state = IWL_TRANS_NO_FW;
 }
@@ -570,6 +590,7 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans,
 
        WARN_ON_ONCE(!trans->rx_mpdu_cmd);
 
+       clear_bit(STATUS_FW_ERROR, &trans->status);
        return trans->ops->start_fw(trans, fw, run_in_rfkill);
 }
 
@@ -601,6 +622,9 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
 {
        int ret;
 
+       if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+               return -EIO;
+
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
                IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
                return -EIO;
@@ -640,6 +664,9 @@ static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
 static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
                               struct iwl_device_cmd *dev_cmd, int queue)
 {
+       if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+               return -EIO;
+
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
                IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
 
@@ -760,7 +787,8 @@ static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr,
 
 static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
 {
-       trans->ops->set_pmi(trans, state);
+       if (trans->ops->set_pmi)
+               trans->ops->set_pmi(trans, state);
 }
 
 static inline void
@@ -780,6 +808,16 @@ iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
        __release(nic_access);
 }
 
+static inline void iwl_trans_fw_error(struct iwl_trans *trans)
+{
+       if (WARN_ON_ONCE(!trans->op_mode))
+               return;
+
+       /* prevent double restarts due to the same erroneous FW */
+       if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status))
+               iwl_op_mode_nic_error(trans->op_mode);
+}
+
 /*****************************************************
 * driver (transport) register/unregister functions
 ******************************************************/
index 285d8c7..f98ec2b 100644 (file)
@@ -1,10 +1,9 @@
 obj-$(CONFIG_IWLMVM)   += iwlmvm.o
 iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
-iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
+iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
 iwlmvm-y += scan.o time-event.o rs.o
 iwlmvm-y += power.o power_legacy.o bt-coex.o
 iwlmvm-y += led.o tt.o
-iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
 
index 93fd145..57d3eed 100644 (file)
@@ -183,15 +183,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
                return -EINVAL;
 
+       /*
+        * Update SF - Disable if needed. if this fails, SF might still be on
+        * while many macs are bound, which is forbidden - so fail the binding.
+        */
+       if (iwl_mvm_sf_update(mvm, vif, false))
+               return -EINVAL;
+
        return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
 }
 
 int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
 
        if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
                return -EINVAL;
 
-       return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
+       ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
+
+       if (!ret)
+               if (iwl_mvm_sf_update(mvm, vif, true))
+                       IWL_ERR(mvm, "Failed to update SF state\n");
+
+       return ret;
 }
index 27f140c..b866757 100644 (file)
 #include "mvm.h"
 #include "debugfs.h"
 
+static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                enum iwl_dbgfs_pm_mask param, int val)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
+
+       dbgfs_pm->mask |= param;
+
+       switch (param) {
+       case MVM_DEBUGFS_PM_KEEP_ALIVE: {
+               struct ieee80211_hw *hw = mvm->hw;
+               int dtimper = hw->conf.ps_dtim_period ?: 1;
+               int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+               IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
+               if (val * MSEC_PER_SEC < 3 * dtimper_msec)
+                       IWL_WARN(mvm,
+                                "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
+                                val * MSEC_PER_SEC, 3 * dtimper_msec);
+               dbgfs_pm->keep_alive_seconds = val;
+               break;
+       }
+       case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
+               IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
+                               val ? "enabled" : "disabled");
+               dbgfs_pm->skip_over_dtim = val;
+               break;
+       case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
+               IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
+               dbgfs_pm->skip_dtim_periods = val;
+               break;
+       case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
+               IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
+               dbgfs_pm->rx_data_timeout = val;
+               break;
+       case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
+               IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
+               dbgfs_pm->tx_data_timeout = val;
+               break;
+       case MVM_DEBUGFS_PM_DISABLE_POWER_OFF:
+               IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val);
+               dbgfs_pm->disable_power_off = val;
+               break;
+       case MVM_DEBUGFS_PM_LPRX_ENA:
+               IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
+               dbgfs_pm->lprx_ena = val;
+               break;
+       case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
+               IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
+               dbgfs_pm->lprx_rssi_threshold = val;
+               break;
+       case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
+               IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
+               dbgfs_pm->snooze_ena = val;
+               break;
+       case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
+               IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
+               dbgfs_pm->uapsd_misbehaving = val;
+               break;
+       }
+}
+
+static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       enum iwl_dbgfs_pm_mask param;
+       int val, ret;
+
+       if (!strncmp("keep_alive=", buf, 11)) {
+               if (sscanf(buf + 11, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_KEEP_ALIVE;
+       } else if (!strncmp("skip_over_dtim=", buf, 15)) {
+               if (sscanf(buf + 15, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
+       } else if (!strncmp("skip_dtim_periods=", buf, 18)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
+       } else if (!strncmp("rx_data_timeout=", buf, 16)) {
+               if (sscanf(buf + 16, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
+       } else if (!strncmp("tx_data_timeout=", buf, 16)) {
+               if (sscanf(buf + 16, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
+       } else if (!strncmp("disable_power_off=", buf, 18) &&
+                  !(mvm->fw->ucode_capa.flags &
+                    IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF;
+       } else if (!strncmp("lprx=", buf, 5)) {
+               if (sscanf(buf + 5, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_LPRX_ENA;
+       } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
+               if (sscanf(buf + 20, "%d", &val) != 1)
+                       return -EINVAL;
+               if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
+                   POWER_LPRX_RSSI_THRESHOLD_MIN)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
+       } else if (!strncmp("snooze_enable=", buf, 14)) {
+               if (sscanf(buf + 14, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
+       } else if (!strncmp("uapsd_misbehaving=", buf, 18)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
+       } else {
+               return -EINVAL;
+       }
+
+       mutex_lock(&mvm->mutex);
+       iwl_dbgfs_update_pm(mvm, vif, param, val);
+       ret = iwl_mvm_power_update_mode(mvm, vif);
+       mutex_unlock(&mvm->mutex);
+
+       return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       char buf[512];
+       int bufsz = sizeof(buf);
+       int pos;
+
+       pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
 static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
                                         char __user *user_buf,
                                         size_t count, loff_t *ppos)
@@ -125,6 +269,201 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
+static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
+                               enum iwl_dbgfs_bf_mask param, int value)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+       dbgfs_bf->mask |= param;
+
+       switch (param) {
+       case MVM_DEBUGFS_BF_ENERGY_DELTA:
+               dbgfs_bf->bf_energy_delta = value;
+               break;
+       case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
+               dbgfs_bf->bf_roaming_energy_delta = value;
+               break;
+       case MVM_DEBUGFS_BF_ROAMING_STATE:
+               dbgfs_bf->bf_roaming_state = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
+               dbgfs_bf->bf_temp_threshold = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
+               dbgfs_bf->bf_temp_fast_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
+               dbgfs_bf->bf_temp_slow_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
+               dbgfs_bf->bf_enable_beacon_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_DEBUG_FLAG:
+               dbgfs_bf->bf_debug_flag = value;
+               break;
+       case MVM_DEBUGFS_BF_ESCAPE_TIMER:
+               dbgfs_bf->bf_escape_timer = value;
+               break;
+       case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
+               dbgfs_bf->ba_enable_beacon_abort = value;
+               break;
+       case MVM_DEBUGFS_BA_ESCAPE_TIMER:
+               dbgfs_bf->ba_escape_timer = value;
+               break;
+       }
+}
+
+static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       enum iwl_dbgfs_bf_mask param;
+       int value, ret = 0;
+
+       if (!strncmp("bf_energy_delta=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ENERGY_DELTA_MIN ||
+                   value > IWL_BF_ENERGY_DELTA_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ENERGY_DELTA;
+       } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
+               if (sscanf(buf+24, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
+                   value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
+       } else if (!strncmp("bf_roaming_state=", buf, 17)) {
+               if (sscanf(buf+17, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ROAMING_STATE_MIN ||
+                   value > IWL_BF_ROAMING_STATE_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ROAMING_STATE;
+       } else if (!strncmp("bf_temp_threshold=", buf, 18)) {
+               if (sscanf(buf+18, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
+                   value > IWL_BF_TEMP_THRESHOLD_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
+       } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
+                   value > IWL_BF_TEMP_FAST_FILTER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
+       } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
+                   value > IWL_BF_TEMP_SLOW_FILTER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
+       } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
+               if (sscanf(buf+24, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
+       } else if (!strncmp("bf_debug_flag=", buf, 14)) {
+               if (sscanf(buf+14, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_DEBUG_FLAG;
+       } else if (!strncmp("bf_escape_timer=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ESCAPE_TIMER_MIN ||
+                   value > IWL_BF_ESCAPE_TIMER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
+       } else if (!strncmp("ba_escape_timer=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BA_ESCAPE_TIMER_MIN ||
+                   value > IWL_BA_ESCAPE_TIMER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
+       } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
+               if (sscanf(buf+23, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
+       } else {
+               return -EINVAL;
+       }
+
+       mutex_lock(&mvm->mutex);
+       iwl_dbgfs_update_bf(vif, param, value);
+       if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
+               ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+       else
+               ret = iwl_mvm_enable_beacon_filter(mvm, vif);
+       mutex_unlock(&mvm->mutex);
+
+       return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       char buf[256];
+       int pos = 0;
+       const size_t bufsz = sizeof(buf);
+       struct iwl_beacon_filter_cmd cmd = {
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter =
+                       cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
+               .ba_enable_beacon_abort =
+                       cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
+       };
+
+       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+       if (mvmvif->bf_data.bf_enabled)
+               cmd.bf_enable_beacon_filter = cpu_to_le32(1);
+       else
+               cmd.bf_enable_beacon_filter = 0;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
+                        le32_to_cpu(cmd.bf_energy_delta));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
+                        le32_to_cpu(cmd.bf_roaming_energy_delta));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
+                        le32_to_cpu(cmd.bf_roaming_state));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
+                        le32_to_cpu(cmd.bf_temp_threshold));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_fast_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_slow_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
+                        le32_to_cpu(cmd.bf_enable_beacon_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
+                        le32_to_cpu(cmd.bf_debug_flag));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
+                        le32_to_cpu(cmd.bf_escape_timer));
+       pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
+                        le32_to_cpu(cmd.ba_escape_timer));
+       pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
+                        le32_to_cpu(cmd.ba_enable_beacon_abort));
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
+       _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
+       _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 #define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do {              \
                if (!debugfs_create_file(#name, mode, parent, vif,      \
                                         &iwl_dbgfs_##name##_ops))      \
@@ -132,6 +471,8 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
        } while (0)
 
 MVM_DEBUGFS_READ_FILE_OPS(mac_params);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
 
 void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
@@ -155,9 +496,21 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                return;
        }
 
+       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+           ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
+            (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
+             mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS)))
+               MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
+                                        S_IRUSR);
+
        MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
                                 S_IRUSR);
 
+       if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+           mvmvif == mvm->bf_allowed_vif)
+               MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
+                                        S_IRUSR | S_IWUSR);
+
        /*
         * Create symlink for convenience pointing to interface specific
         * debugfs entries for the driver. For example, under
index 5cb93ae..cb78e55 100644 (file)
@@ -85,6 +85,8 @@
  *             PBW Snoozing enabled
  * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
  * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
+ * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
+ *             detection enablement
 */
 enum iwl_power_flags {
        POWER_FLAGS_POWER_SAVE_ENA_MSK          = BIT(0),
@@ -94,6 +96,7 @@ enum iwl_power_flags {
        POWER_FLAGS_BT_SCO_ENA                  = BIT(8),
        POWER_FLAGS_ADVANCE_PM_ENA_MSK          = BIT(9),
        POWER_FLAGS_LPRX_ENA_MSK                = BIT(11),
+       POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK   = BIT(12),
 };
 
 #define IWL_POWER_VEC_SIZE 5
@@ -228,6 +231,19 @@ struct iwl_mac_power_cmd {
        u8 reserved;
 } __packed;
 
+/*
+ * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
+ * associated AP is identified as improperly implementing uAPSD protocol.
+ * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78
+ * @sta_id: index of station in uCode's station table - associated AP ID in
+ *         this context.
+ */
+struct iwl_uapsd_misbehaving_ap_notif {
+       __le32 sta_id;
+       u8 mac_id;
+       u8 reserved[3];
+} __packed;
+
 /**
  * struct iwl_beacon_filter_cmd
  * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
index 4aca593..8c73ba7 100644 (file)
@@ -138,7 +138,14 @@ enum iwl_sta_flags {
 
 /**
  * enum iwl_sta_key_flag - key flags for the ADD_STA host command
- * @STA_KEY_FLG_EN_MSK: mask for encryption algorithm
+ * @STA_KEY_FLG_NO_ENC: no encryption
+ * @STA_KEY_FLG_WEP: WEP encryption algorithm
+ * @STA_KEY_FLG_CCM: CCMP encryption algorithm
+ * @STA_KEY_FLG_TKIP: TKIP encryption algorithm
+ * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support)
+ * @STA_KEY_FLG_CMAC: CMAC encryption algorithm
+ * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm
+ * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value
  * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
  *     station info array (1 - n 1X mode)
  * @STA_KEY_FLG_KEYID_MSK: the index of the key
@@ -152,6 +159,7 @@ enum iwl_sta_key_flag {
        STA_KEY_FLG_WEP                 = (1 << 0),
        STA_KEY_FLG_CCM                 = (2 << 0),
        STA_KEY_FLG_TKIP                = (3 << 0),
+       STA_KEY_FLG_EXT                 = (4 << 0),
        STA_KEY_FLG_CMAC                = (6 << 0),
        STA_KEY_FLG_ENC_UNKNOWN         = (7 << 0),
        STA_KEY_FLG_EN_MSK              = (7 << 0),
index d606197..2286467 100644 (file)
@@ -132,6 +132,7 @@ enum iwl_tx_flags {
 #define TX_CMD_SEC_WEP                 0x01
 #define TX_CMD_SEC_CCM                 0x02
 #define TX_CMD_SEC_TKIP                        0x03
+#define TX_CMD_SEC_EXT                 0x04
 #define TX_CMD_SEC_MSK                 0x07
 #define TX_CMD_SEC_WEP_KEY_IDX_POS     6
 #define TX_CMD_SEC_WEP_KEY_IDX_MSK     0xc0
index bad5a55..1c30797 100644 (file)
@@ -141,6 +141,7 @@ enum {
 
        /* Power - legacy power table command */
        POWER_TABLE_CMD = 0x77,
+       PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
 
        /* Thermal Throttling*/
        REPLY_THERMAL_MNG_BACKOFF = 0x7e,
@@ -183,6 +184,7 @@ enum {
        BT_PROFILE_NOTIFICATION = 0xce,
        BT_COEX_CI = 0x5d,
 
+       REPLY_SF_CFG_CMD = 0xd1,
        REPLY_BEACON_FILTERING_CMD = 0xd2,
 
        REPLY_DEBUG_CMD = 0xf0,
@@ -1052,6 +1054,7 @@ enum iwl_mvm_rx_status {
        RX_MPDU_RES_STATUS_SEC_WEP_ENC                  = (1 << 8),
        RX_MPDU_RES_STATUS_SEC_CCM_ENC                  = (2 << 8),
        RX_MPDU_RES_STATUS_SEC_TKIP_ENC                 = (3 << 8),
+       RX_MPDU_RES_STATUS_SEC_EXT_ENC                  = (4 << 8),
        RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC             = (6 << 8),
        RX_MPDU_RES_STATUS_SEC_ENC_ERR                  = (7 << 8),
        RX_MPDU_RES_STATUS_SEC_ENC_MSK                  = (7 << 8),
@@ -1131,6 +1134,7 @@ struct iwl_set_calib_default_cmd {
 } __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
 
 #define MAX_PORT_ID_NUM        2
+#define MAX_MCAST_FILTERING_ADDRESSES 256
 
 /**
  * struct iwl_mcast_filter_cmd - configure multicast filter.
@@ -1363,4 +1367,65 @@ struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
        struct mvm_statistics_general general;
 } __packed;
 
+/***********************************
+ * Smart Fifo API
+ ***********************************/
+/* Smart Fifo state */
+enum iwl_sf_state {
+       SF_LONG_DELAY_ON = 0, /* should never be called by driver */
+       SF_FULL_ON,
+       SF_UNINIT,
+       SF_INIT_OFF,
+       SF_HW_NUM_STATES
+};
+
+/* Smart Fifo possible scenario */
+enum iwl_sf_scenario {
+       SF_SCENARIO_SINGLE_UNICAST,
+       SF_SCENARIO_AGG_UNICAST,
+       SF_SCENARIO_MULTICAST,
+       SF_SCENARIO_BA_RESP,
+       SF_SCENARIO_TX_RESP,
+       SF_NUM_SCENARIO
+};
+
+#define SF_TRANSIENT_STATES_NUMBER 2   /* SF_LONG_DELAY_ON and SF_FULL_ON */
+#define SF_NUM_TIMEOUT_TYPES 2         /* Aging timer and Idle timer */
+
+/* smart FIFO default values */
+#define SF_W_MARK_SISO 4096
+#define SF_W_MARK_MIMO2 8192
+#define SF_W_MARK_MIMO3 6144
+#define SF_W_MARK_LEGACY 4096
+#define SF_W_MARK_SCAN 4096
+
+/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */
+#define SF_SINGLE_UNICAST_IDLE_TIMER 320       /* 300 uSec  */
+#define SF_SINGLE_UNICAST_AGING_TIMER 2016     /* 2 mSec */
+#define SF_AGG_UNICAST_IDLE_TIMER 320          /* 300 uSec */
+#define SF_AGG_UNICAST_AGING_TIMER 2016                /* 2 mSec */
+#define SF_MCAST_IDLE_TIMER 2016               /* 2 mSec */
+#define SF_MCAST_AGING_TIMER 10016             /* 10 mSec */
+#define SF_BA_IDLE_TIMER 320                   /* 300 uSec */
+#define SF_BA_AGING_TIMER 2016                 /* 2 mSec */
+#define SF_TX_RE_IDLE_TIMER 320                        /* 300 uSec */
+#define SF_TX_RE_AGING_TIMER 2016              /* 2 mSec */
+
+#define SF_LONG_DELAY_AGING_TIMER 1000000      /* 1 Sec */
+
+/**
+ * Smart Fifo configuration command.
+ * @state: smart fifo state, types listed in iwl_sf_sate.
+ * @watermark: Minimum allowed availabe free space in RXF for transient state.
+ * @long_delay_timeouts: aging and idle timer values for each scenario
+ * in long delay state.
+ * @full_on_timeouts: timer values for each scenario in full on state.
+ */
+struct iwl_sf_cfg_cmd {
+       enum iwl_sf_state state;
+       __le32 watermark[SF_TRANSIENT_STATES_NUMBER];
+       __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
+       __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
+} __packed; /* SF_CFG_API_S_VER_2 */
+
 #endif /* __fw_api_h__ */
index 70e5297..27ba104 100644 (file)
@@ -241,7 +241,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->init_ucode_complete)
+       if (WARN_ON_ONCE(mvm->init_ucode_complete))
                return 0;
 
        iwl_init_notification_wait(&mvm->notif_wait,
@@ -287,7 +287,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                IWL_DEBUG_RF_KILL(mvm,
                                  "jump over all phy activities due to RF kill\n");
                iwl_remove_notification(&mvm->notif_wait, &calib_wait);
-               return 1;
+               ret = 1;
+               goto out;
        }
 
        /* Send TX valid antennas before triggering calibrations */
@@ -319,9 +320,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 error:
        iwl_remove_notification(&mvm->notif_wait, &calib_wait);
 out:
-       if (!iwlmvm_mod_params.init_dbg) {
-               iwl_trans_stop_device(mvm->trans);
-       } else if (!mvm->nvm_data) {
+       if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
                /* we want to debug INIT and we have no NVM - fake */
                mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
                                        sizeof(struct ieee80211_channel) +
@@ -370,11 +369,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                                ret = -ERFKILL;
                        goto error;
                }
-               /* should stop & start HW since that INIT image just loaded */
-               iwl_trans_stop_hw(mvm->trans, false);
-               ret = iwl_trans_start_hw(mvm->trans);
-               if (ret)
-                       return ret;
+               if (!iwlmvm_mod_params.init_dbg) {
+                       /*
+                        * should stop and start HW since that INIT
+                        * image just loaded
+                        */
+                       iwl_trans_stop_device(mvm->trans);
+                       ret = iwl_trans_start_hw(mvm->trans);
+                       if (ret)
+                               return ret;
+               }
        }
 
        if (iwlmvm_mod_params.init_dbg)
@@ -386,6 +390,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                goto error;
        }
 
+       ret = iwl_mvm_sf_update(mvm, NULL, false);
+       if (ret)
+               IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
+
        ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
        if (ret)
                goto error;
index a77007c..2f52693 100644 (file)
@@ -261,6 +261,12 @@ 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 && mvm->fw->cs->cipher) {
+               mvm->hw->n_cipher_schemes = 1;
+               mvm->hw->cipher_schemes = mvm->fw->cs;
+       }
+
 #ifdef CONFIG_PM_SLEEP
        if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
@@ -399,7 +405,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
        iwl_trans_stop_device(mvm->trans);
-       iwl_trans_stop_hw(mvm->trans, false);
 
        mvm->scan_status = IWL_MVM_SCAN_NONE;
 
@@ -471,7 +476,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        cancel_work_sync(&mvm->roc_done_wk);
 
        iwl_trans_stop_device(mvm->trans);
-       iwl_trans_stop_hw(mvm->trans, false);
 
        iwl_mvm_async_handlers_purge(mvm);
        /* async_handlers_list is empty and will stay empty: HW is stopped */
@@ -488,17 +492,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        cancel_work_sync(&mvm->async_handlers_wk);
 }
 
-static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac,
-                                       struct ieee80211_vif *vif)
-{
-       struct iwl_mvm *mvm = data;
-       int ret;
-
-       ret = iwl_mvm_power_disable(mvm, vif);
-       if (ret)
-               IWL_ERR(mvm, "failed to disable power management\n");
-}
-
 static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
                                          struct ieee80211_vif *vif)
 {
@@ -521,6 +514,20 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
        return NULL;
 }
 
+static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               s8 tx_power)
+{
+       /* FW is in charge of regulatory enforcement */
+       struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
+               .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
+               .pwr_restriction = cpu_to_le16(tx_power),
+       };
+
+       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
+                                   sizeof(reduce_txpwr_cmd),
+                                   &reduce_txpwr_cmd);
+}
+
 static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
@@ -541,26 +548,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_unlock;
 
-       /*
-        * TODO: remove this temporary code.
-        * Currently MVM FW supports power management only on single MAC.
-        * If new interface added, disable PM on existing interface.
-        * P2P device is a special case, since it is handled by FW similary to
-        * scan. If P2P deviced is added, PM remains enabled on existing
-        * interface.
-        * Note: the method below does not count the new interface being added
-        * at this moment.
-        */
+       /* Counting number of interfaces is needed for legacy PM */
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count++;
-       if (mvm->vif_count > 1) {
-               IWL_DEBUG_MAC80211(mvm,
-                                  "Disable power on existing interfaces\n");
-               ieee80211_iterate_active_interfaces_atomic(
-                                           mvm->hw,
-                                           IEEE80211_IFACE_ITER_NORMAL,
-                                           iwl_mvm_pm_disable_iterator, mvm);
-       }
 
        /*
         * The AP binding flow can be done only after the beacon
@@ -591,11 +581,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_release;
 
-       /*
-        * Update power state on the new interface. Admittedly, based on
-        * mac80211 logics this power update will disable power management
-        */
-       iwl_mvm_power_update_mode(mvm, vif);
+       iwl_mvm_power_disable(mvm, vif);
 
        /* beacon filtering */
        ret = iwl_mvm_disable_beacon_filter(mvm, vif);
@@ -656,9 +642,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
  out_release:
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
+
+       /* TODO: remove this when legacy PM will be discarded */
        ieee80211_iterate_active_interfaces(
                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                iwl_mvm_power_update_iterator, mvm);
+
        iwl_mvm_mac_ctxt_release(mvm, vif);
  out_unlock:
        mutex_unlock(&mvm->mutex);
@@ -744,21 +733,13 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
                mvmvif->phy_ctxt = NULL;
        }
 
-       /*
-        * TODO: remove this temporary code.
-        * Currently MVM FW supports power management only on single MAC.
-        * Check if only one additional interface remains after removing
-        * current one. Update power mode on the remaining interface.
-        */
        if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
-       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
-                          mvm->vif_count);
-       if (mvm->vif_count == 1) {
-               ieee80211_iterate_active_interfaces(
-                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-                                       iwl_mvm_power_update_iterator, mvm);
-       }
+
+       /* TODO: remove this when legacy PM will be discarded */
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_power_update_iterator, mvm);
 
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
@@ -767,23 +748,91 @@ out_release:
        mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                               s8 tx_power)
+static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
 {
-       /* FW is in charge of regulatory enforcement */
-       struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
-               .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
-               .pwr_restriction = cpu_to_le16(tx_power),
+       return 0;
+}
+
+struct iwl_mvm_mc_iter_data {
+       struct iwl_mvm *mvm;
+       int port_id;
+};
+
+static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_mc_iter_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+       struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
+       int ret, len;
+
+       /* if we don't have free ports, mcast frames will be dropped */
+       if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM))
+               return;
+
+       if (vif->type != NL80211_IFTYPE_STATION ||
+           !vif->bss_conf.assoc)
+               return;
+
+       cmd->port_id = data->port_id++;
+       memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
+       len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, len, cmd);
+       if (ret)
+               IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
+}
+
+static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_mc_iter_data iter_data = {
+               .mvm = mvm,
        };
 
-       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
-                                   sizeof(reduce_txpwr_cmd),
-                                   &reduce_txpwr_cmd);
+       lockdep_assert_held(&mvm->mutex);
+
+       if (WARN_ON_ONCE(!mvm->mcast_filter_cmd))
+               return;
+
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_mc_iface_iterator, &iter_data);
 }
 
-static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
+static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
+                                    struct netdev_hw_addr_list *mc_list)
 {
-       return 0;
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mcast_filter_cmd *cmd;
+       struct netdev_hw_addr *addr;
+       int addr_count = netdev_hw_addr_list_count(mc_list);
+       bool pass_all = false;
+       int len;
+
+       if (addr_count > MAX_MCAST_FILTERING_ADDRESSES) {
+               pass_all = true;
+               addr_count = 0;
+       }
+
+       len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4);
+       cmd = kzalloc(len, GFP_ATOMIC);
+       if (!cmd)
+               return 0;
+
+       if (pass_all) {
+               cmd->pass_all = 1;
+               return (u64)(unsigned long)cmd;
+       }
+
+       netdev_hw_addr_list_for_each(addr, mc_list) {
+               IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n",
+                                  cmd->count, addr->addr);
+               memcpy(&cmd->addr_list[cmd->count * ETH_ALEN],
+                      addr->addr, ETH_ALEN);
+               cmd->count++;
+       }
+
+       return (u64)(unsigned long)cmd;
 }
 
 static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
@@ -791,21 +840,22 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
                                     unsigned int *total_flags,
                                     u64 multicast)
 {
-       *total_flags = 0;
-}
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
 
-static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm,
-                                         struct ieee80211_vif *vif)
-{
-       struct iwl_mcast_filter_cmd mcast_filter_cmd = {
-               .pass_all = 1,
-       };
+       mutex_lock(&mvm->mutex);
+
+       /* replace previous configuration */
+       kfree(mvm->mcast_filter_cmd);
+       mvm->mcast_filter_cmd = cmd;
 
-       memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN);
+       if (!cmd)
+               goto out;
 
-       return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC,
-                                   sizeof(mcast_filter_cmd),
-                                   &mcast_filter_cmd);
+       iwl_mvm_recalc_multicast(mvm);
+out:
+       mutex_unlock(&mvm->mutex);
+       *total_flags = 0;
 }
 
 static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
@@ -828,7 +878,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
                        }
-                       iwl_mvm_configure_mcast_filter(mvm, vif);
 
                        if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
                                     &mvm->status)) {
@@ -850,7 +899,17 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                iwl_mvm_protect_session(mvm, vif, dur, dur,
                                                        5 * dur);
                        }
+
+                       iwl_mvm_sf_update(mvm, vif, false);
+                       iwl_mvm_power_vif_assoc(mvm, vif);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+                       /*
+                        * If update fails - SF might be running in associated
+                        * mode while disassociated - which is forbidden.
+                        */
+                       WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
+                                 "Failed to update SF upon disassociation\n");
+
                        /* remove AP station now that the MAC is unassoc */
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
                        if (ret)
@@ -862,6 +921,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
 
+               iwl_mvm_recalc_multicast(mvm);
+
                /* reset rssi values */
                mvmvif->bf_data.ave_beacon_signal = 0;
 
@@ -882,7 +943,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                 */
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
-       } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) {
+       } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
+                             BSS_CHANGED_QOS)) {
                ret = iwl_mvm_power_update_mode(mvm, vif);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
@@ -991,11 +1053,16 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
                                 struct ieee80211_bss_conf *bss_conf,
                                 u32 changes)
 {
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT |
                                              BSS_CHANGED_HT |
                                              BSS_CHANGED_BANDWIDTH;
        int ret;
 
+       /* Changes will be applied when the AP/IBSS is started */
+       if (!mvmvif->ap_ibss_active)
+               return;
+
        if (changes & ht_change) {
                ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
                if (ret)
@@ -1222,6 +1289,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        return 0;
 }
 
+static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sta *sta, u32 changed)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           changed & IEEE80211_RC_NSS_CHANGED)
+               iwl_mvm_sf_update(mvm, vif, false);
+}
+
 static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif, u16 ac,
                               const struct ieee80211_tx_queue_params *params)
@@ -1344,7 +1422,12 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
                 */
                return 0;
        default:
-               return -EOPNOTSUPP;
+               /* currently FW supports only one optional cipher scheme */
+               if (hw->n_cipher_schemes &&
+                   hw->cipher_schemes->cipher == key->cipher)
+                       key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+               else
+                       return -EOPNOTSUPP;
        }
 
        mutex_lock(&mvm->mutex);
@@ -1550,7 +1633,7 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
                goto out;
        }
 
-       ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+       ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
                                       ctx->rx_chains_static,
                                       ctx->rx_chains_dynamic);
        if (ret) {
@@ -1594,7 +1677,7 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
                return;
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+       iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
                                 ctx->rx_chains_static,
                                 ctx->rx_chains_dynamic);
        iwl_mvm_bt_coex_vif_change(mvm);
@@ -1637,7 +1720,13 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                goto out_unlock;
 
        /*
-        * Setting the quota at this stage is only required for monitor
+        * Power state must be updated before quotas,
+        * otherwise fw will complain.
+        */
+       mvm->bound_vif_cnt++;
+       iwl_mvm_power_update_binding(mvm, vif, true);
+
+       /* Setting the quota at this stage is only required for monitor
         * interfaces. For the other types, the bss_info changed flow
         * will handle quota settings.
         */
@@ -1652,6 +1741,8 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 
  out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
+       mvm->bound_vif_cnt--;
+       iwl_mvm_power_update_binding(mvm, vif, false);
  out_unlock:
        mutex_unlock(&mvm->mutex);
        if (ret)
@@ -1685,6 +1776,9 @@ 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);
+
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1779,6 +1873,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .add_interface = iwl_mvm_mac_add_interface,
        .remove_interface = iwl_mvm_mac_remove_interface,
        .config = iwl_mvm_mac_config,
+       .prepare_multicast = iwl_mvm_prepare_multicast,
        .configure_filter = iwl_mvm_configure_filter,
        .bss_info_changed = iwl_mvm_bss_info_changed,
        .hw_scan = iwl_mvm_mac_hw_scan,
@@ -1788,6 +1883,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .sta_notify = iwl_mvm_mac_sta_notify,
        .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
        .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
+       .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
        .sched_scan_start = iwl_mvm_mac_sched_scan_start,
index 7dc57cf..84edf36 100644 (file)
@@ -163,6 +163,8 @@ struct iwl_mvm_power_ops {
                                 struct ieee80211_vif *vif);
        int (*power_update_device_mode)(struct iwl_mvm *mvm);
        int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+       void (*power_update_binding)(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif, bool assign);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                char *buf, int bufsz);
@@ -181,6 +183,7 @@ enum iwl_dbgfs_pm_mask {
        MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
        MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
        MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
+       MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9),
 };
 
 struct iwl_dbgfs_pm {
@@ -193,6 +196,7 @@ struct iwl_dbgfs_pm {
        bool lprx_ena;
        u32 lprx_rssi_threshold;
        bool snooze_ena;
+       bool uapsd_misbehaving;
        int mask;
 };
 
@@ -269,8 +273,8 @@ struct iwl_mvm_vif_bf_data {
  * @bcast_sta: station used for broadcast packets. Used by the following
  *  vifs: P2P_DEVICE, GO and AP.
  * @beacon_skb: the skb used to hold the AP/GO beacon template
- * @smps_requests: the requests of of differents parts of the driver, regard
      the desired smps mode.
+ * @smps_requests: the SMPS requests of differents parts of the driver,
*     combined on update to yield the overall request to mac80211.
  */
 struct iwl_mvm_vif {
        u16 id;
@@ -331,6 +335,11 @@ struct iwl_mvm_vif {
 #endif
 
        enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
+
+       /* FW identified misbehaving AP */
+       u8 uapsd_misbehaving_bssid[ETH_ALEN];
+
+       bool pm_prevented;
 };
 
 static inline struct iwl_mvm_vif *
@@ -479,6 +488,7 @@ struct iwl_mvm {
        /* Scan status, cmd (pre-allocated) and auxiliary station */
        enum iwl_scan_status scan_status;
        struct iwl_scan_cmd *scan_cmd;
+       struct iwl_mcast_filter_cmd *mcast_filter_cmd;
 
        /* rx chain antennas set through debugfs for the scan command */
        u8 scan_rx_ant;
@@ -489,6 +499,9 @@ struct iwl_mvm {
        u8 scan_last_antenna_idx; /* to toggle TX between antennas */
        u8 mgmt_last_antenna_idx;
 
+       /* last smart fifo state that was successfully sent to firmware */
+       enum iwl_sf_state sf_state;
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        struct dentry *debugfs_dir;
        u32 dbgfs_sram_offset, dbgfs_sram_len;
@@ -512,12 +525,6 @@ struct iwl_mvm {
         */
        unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
 
-       /*
-        * This counter of created interfaces is referenced only in conjunction
-        * with FW limitation related to power management. Currently PM is
-        * supported only on a single interface.
-        * IMPORTANT: this variable counts all interfaces except P2P device.
-        */
        u8 vif_count;
 
        /* -1 for always, 0 for never, >0 for that many times */
@@ -560,6 +567,11 @@ struct iwl_mvm {
        u8 aux_queue;
        u8 first_agg_queue;
        u8 last_agg_queue;
+
+       u8 bound_vif_cnt;
+
+       /* Indicate if device power save is allowed */
+       bool ps_prevented;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -778,6 +790,19 @@ static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
        return 0;
 }
 
+static inline void iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
+                                               struct ieee80211_vif *vif,
+                                               bool assign)
+{
+       if (mvm->pm_ops->power_update_binding)
+               mvm->pm_ops->power_update_binding(mvm, vif, assign);
+}
+
+void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
+                                            struct iwl_rx_cmd_buffer *rxb,
+                                            struct iwl_device_cmd *cmd);
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
                                            struct ieee80211_vif *vif,
@@ -869,4 +894,8 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
 void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
 void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
 
+/* smart fifo */
+int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                     bool added_vif);
+
 #endif /* __IWL_MVM_H__ */
index d86083c..a362430 100644 (file)
@@ -236,6 +236,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
                   false),
 
        RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
+       RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
+                  iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
 };
 #undef RX_HANDLER
 #define CMD(x) [x] = #x
@@ -311,6 +313,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(REPLY_THERMAL_MNG_BACKOFF),
        CMD(MAC_PM_POWER_TABLE),
        CMD(BT_COEX_CI),
+       CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
 };
 #undef CMD
 
@@ -341,7 +344,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        op_mode = hw->priv;
        op_mode->ops = &iwl_mvm_ops;
-       op_mode->trans = trans;
 
        mvm = IWL_OP_MODE_GET_MVM(op_mode);
        mvm->dev = trans->dev;
@@ -359,6 +361,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                mvm->aux_queue = 11;
                mvm->first_agg_queue = 12;
        }
+       mvm->sf_state = SF_UNINIT;
 
        mutex_init(&mvm->mutex);
        spin_lock_init(&mvm->async_handlers_lock);
@@ -424,7 +427,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
         * there is no need to unnecessarily power up the NIC at driver load
         */
        if (iwlwifi_mod_params.nvm_file) {
-                       iwl_nvm_init(mvm);
+               err = iwl_nvm_init(mvm);
+               if (err)
+                       goto out_free;
        } else {
                err = iwl_trans_start_hw(mvm->trans);
                if (err)
@@ -432,16 +437,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
                mutex_lock(&mvm->mutex);
                err = iwl_run_init_mvm_ucode(mvm, true);
+               iwl_trans_stop_device(trans);
                mutex_unlock(&mvm->mutex);
                /* returns 0 if successful, 1 if success but in rfkill */
                if (err < 0 && !iwlmvm_mod_params.init_dbg) {
                        IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
                        goto out_free;
                }
-
-               /* Stop the hw after the ALIVE and NVM has been read */
-               if (!iwlmvm_mod_params.init_dbg)
-                       iwl_trans_stop_hw(mvm->trans, false);
        }
 
        scan_size = sizeof(struct iwl_scan_cmd) +
@@ -474,7 +476,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        iwl_phy_db_free(mvm->phy_db);
        kfree(mvm->scan_cmd);
        if (!iwlwifi_mod_params.nvm_file)
-               iwl_trans_stop_hw(trans, true);
+               iwl_trans_op_mode_leave(trans);
        ieee80211_free_hw(mvm->hw);
        return NULL;
 }
@@ -491,12 +493,14 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        ieee80211_unregister_hw(mvm->hw);
 
        kfree(mvm->scan_cmd);
+       kfree(mvm->mcast_filter_cmd);
+       mvm->mcast_filter_cmd = NULL;
 
 #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
        kfree(mvm->d3_resume_sram);
 #endif
 
-       iwl_trans_stop_hw(mvm->trans, true);
+       iwl_trans_op_mode_leave(mvm->trans);
 
        iwl_phy_db_free(mvm->phy_db);
        mvm->phy_db = NULL;
index 550824a..483ecc6 100644 (file)
@@ -186,6 +186,92 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
        }
 }
 
+static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_mac_power_cmd *cmd)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       enum ieee80211_ac_numbers ac;
+       bool tid_found = false;
+
+       for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
+               if (!mvmvif->queue_params[ac].uapsd)
+                       continue;
+
+               if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+
+               cmd->uapsd_ac_flags |= BIT(ac);
+
+               /* QNDP TID - the highest TID with no admission control */
+               if (!tid_found && !mvmvif->queue_params[ac].acm) {
+                       tid_found = true;
+                       switch (ac) {
+                       case IEEE80211_AC_VO:
+                               cmd->qndp_tid = 6;
+                               break;
+                       case IEEE80211_AC_VI:
+                               cmd->qndp_tid = 5;
+                               break;
+                       case IEEE80211_AC_BE:
+                               cmd->qndp_tid = 0;
+                               break;
+                       case IEEE80211_AC_BK:
+                               cmd->qndp_tid = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
+
+       if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
+                                   BIT(IEEE80211_AC_VI) |
+                                   BIT(IEEE80211_AC_BE) |
+                                   BIT(IEEE80211_AC_BK))) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+               cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
+               cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
+                       cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
+                       cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
+       }
+
+       cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
+
+       if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
+           cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+               cmd->rx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+       } else {
+               cmd->rx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
+       }
+
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+               cmd->heavy_tx_thld_packets =
+                       IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
+               cmd->heavy_rx_thld_packets =
+                       IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
+       } else {
+               cmd->heavy_tx_thld_packets =
+                       IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
+               cmd->heavy_rx_thld_packets =
+                       IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+       }
+       cmd->heavy_tx_thld_percentage =
+               IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
+       cmd->heavy_rx_thld_percentage =
+               IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
+}
+
 static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
                                    struct iwl_mac_power_cmd *cmd)
@@ -198,8 +284,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        bool radar_detect = false;
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
-       enum ieee80211_ac_numbers ac;
-       bool tid_found = false;
+       bool allow_uapsd = true;
 
        cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
                                                            mvmvif->color));
@@ -217,7 +302,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
        cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
+           mvm->ps_prevented)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
@@ -227,7 +313,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
            mvmvif->dbgfs_pm.disable_power_off)
                cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
 #endif
-       if (!vif->bss_conf.ps)
+       if (!vif->bss_conf.ps || mvmvif->pm_prevented)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -269,81 +355,24 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                        cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
        }
 
-       for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
-               if (!mvmvif->queue_params[ac].uapsd)
-                       continue;
-
-               if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
-                       cmd->flags |=
-                               cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
-
-               cmd->uapsd_ac_flags |= BIT(ac);
+       if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+                   ETH_ALEN))
+               allow_uapsd = false;
 
-               /* QNDP TID - the highest TID with no admission control */
-               if (!tid_found && !mvmvif->queue_params[ac].acm) {
-                       tid_found = true;
-                       switch (ac) {
-                       case IEEE80211_AC_VO:
-                               cmd->qndp_tid = 6;
-                               break;
-                       case IEEE80211_AC_VI:
-                               cmd->qndp_tid = 5;
-                               break;
-                       case IEEE80211_AC_BE:
-                               cmd->qndp_tid = 0;
-                               break;
-                       case IEEE80211_AC_BK:
-                               cmd->qndp_tid = 1;
-                               break;
-                       }
-               }
-       }
-
-       if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
-               if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
-                                           BIT(IEEE80211_AC_VI) |
-                                           BIT(IEEE80211_AC_BE) |
-                                           BIT(IEEE80211_AC_BK))) {
-                       cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
-                       cmd->snooze_interval =
-                               cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
-                       cmd->snooze_window =
-                               (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
-                               cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
-                               cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
-               }
-
-               cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
-
-               if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
-                   cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
-                       cmd->rx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
-                       cmd->tx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
-               } else {
-                       cmd->rx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
-                       cmd->tx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
-               }
+       if (vif->p2p &&
+           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
+               allow_uapsd = false;
+       /*
+        * Avoid using uAPSD if P2P client is associated to GO that uses
+        * opportunistic power save. This is due to current FW limitation.
+        */
+       if (vif->p2p &&
+           vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
+           IEEE80211_P2P_OPPPS_ENABLE_BIT)
+               allow_uapsd = false;
 
-               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
-                       cmd->heavy_tx_thld_packets =
-                               IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
-                       cmd->heavy_rx_thld_packets =
-                               IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
-               } else {
-                       cmd->heavy_tx_thld_packets =
-                               IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
-                       cmd->heavy_rx_thld_packets =
-                               IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
-               }
-               cmd->heavy_tx_thld_percentage =
-                       IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
-               cmd->heavy_rx_thld_percentage =
-                       IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
-       }
+       if (allow_uapsd)
+               iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
@@ -381,6 +410,13 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                        cmd->flags &=
                                cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
        }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
+               u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
+               if (mvmvif->dbgfs_pm.uapsd_misbehaving)
+                       cmd->flags |= cpu_to_le16(flag);
+               else
+                       cmd->flags &= cpu_to_le16(flag);
+       }
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 }
 
@@ -391,18 +427,11 @@ static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
        bool ba_enable;
        struct iwl_mac_power_cmd cmd = {};
 
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       if (vif->type != NL80211_IFTYPE_STATION)
                return 0;
 
-       /*
-        * TODO: The following vif_count verification is temporary condition.
-        * Avoid power mode update if more than one interface is currently
-        * active. Remove this condition when FW will support power management
-        * on multiple MACs.
-        */
-       IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n",
-                       mvm->vif_count);
-       if (mvm->vif_count > 1)
+       if (vif->p2p &&
+           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS))
                return 0;
 
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
@@ -446,7 +475,7 @@ static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
                                    sizeof(cmd), &cmd);
 }
 
-static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
+static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
 {
        struct iwl_device_power_cmd cmd = {
                .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
@@ -455,7 +484,8 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
                return 0;
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
+           force_disable)
                cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -472,6 +502,78 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
                                    &cmd);
 }
 
+static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
+{
+       return _iwl_mvm_power_update_device(mvm, false);
+}
+
+void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid,
+                  ETH_ALEN))
+               memset(mvmvif->uapsd_misbehaving_bssid, 0, ETH_ALEN);
+}
+
+static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,
+                                                    struct ieee80211_vif *vif)
+{
+       u8 *ap_sta_id = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       /* The ap_sta_id is not expected to change during current association
+        * so no explicit protection is needed
+        */
+       if (mvmvif->ap_sta_id == *ap_sta_id)
+               memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+                      ETH_ALEN);
+}
+
+int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
+                                            struct iwl_rx_cmd_buffer *rxb,
+                                            struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
+       u8 ap_sta_id = le32_to_cpu(notif->sta_id);
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id);
+
+       return 0;
+}
+
+static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
+                                          struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = _data;
+       int ret;
+
+       mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
+
+       ret = iwl_mvm_power_mac_update_mode(mvm, vif);
+       WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
+}
+
+static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         bool assign)
+{
+       if (vif->type == NL80211_IFTYPE_MONITOR) {
+               int ret = _iwl_mvm_power_update_device(mvm, assign);
+               mvm->ps_prevented = assign;
+               WARN_ONCE(ret, "Failed to update power device state\n");
+       }
+
+       ieee80211_iterate_active_interfaces(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_power_binding_iterator,
+                                           mvm);
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif, char *buf,
@@ -494,70 +596,58 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
        pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
                         le16_to_cpu(cmd.keep_alive_seconds));
 
-       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
-               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
-                                (cmd.flags &
-                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
-                                1 : 0);
-               pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
-                                cmd.skip_dtim_periods);
-               if (!(cmd.flags &
-                     cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "rx_data_timeout = %d\n",
-                                        le32_to_cpu(cmd.rx_data_timeout));
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "tx_data_timeout = %d\n",
-                                        le32_to_cpu(cmd.tx_data_timeout));
-               }
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "lprx_rssi_threshold = %d\n",
-                                        cmd.lprx_rssi_threshold);
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
-                       pos +=
-                       scnprintf(buf+pos, bufsz-pos,
-                                 "rx_data_timeout_uapsd = %d\n",
-                                 le32_to_cpu(cmd.rx_data_timeout_uapsd));
-                       pos +=
-                       scnprintf(buf+pos, bufsz-pos,
-                                 "tx_data_timeout_uapsd = %d\n",
-                                 le32_to_cpu(cmd.tx_data_timeout_uapsd));
-                       pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n",
-                                        cmd.qndp_tid);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "uapsd_ac_flags = 0x%x\n",
-                                        cmd.uapsd_ac_flags);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "uapsd_max_sp = %d\n",
-                                        cmd.uapsd_max_sp);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_tx_thld_packets = %d\n",
-                                        cmd.heavy_tx_thld_packets);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_rx_thld_packets = %d\n",
-                                        cmd.heavy_rx_thld_packets);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_tx_thld_percentage = %d\n",
-                                        cmd.heavy_tx_thld_percentage);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_rx_thld_percentage = %d\n",
-                                        cmd.heavy_rx_thld_percentage);
-                       pos +=
-                       scnprintf(buf+pos, bufsz-pos, "snooze_enable = %d\n",
-                                 (cmd.flags &
-                                  cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) ?
-                                 1 : 0);
-               }
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "snooze_interval = %d\n",
-                                        cmd.snooze_interval);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "snooze_window = %d\n",
-                                        cmd.snooze_window);
-               }
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)))
+               return pos;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+                        (cmd.flags &
+                        cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0);
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+                        cmd.skip_dtim_periods);
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
+               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.rx_data_timeout));
+               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.tx_data_timeout));
        }
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "lprx_rssi_threshold = %d\n",
+                                cmd.lprx_rssi_threshold);
+
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
+               return pos;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n",
+                        le32_to_cpu(cmd.rx_data_timeout_uapsd));
+       pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n",
+                        le32_to_cpu(cmd.tx_data_timeout_uapsd));
+       pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid);
+       pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n",
+                        cmd.uapsd_ac_flags);
+       pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n",
+                        cmd.uapsd_max_sp);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n",
+                        cmd.heavy_tx_thld_packets);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n",
+                        cmd.heavy_rx_thld_packets);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n",
+                        cmd.heavy_tx_thld_percentage);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n",
+                        cmd.heavy_rx_thld_percentage);
+       pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n",
+                        (cmd.flags &
+                         cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ?
+                        1 : 0);
+
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)))
+               return pos;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n",
+                        cmd.snooze_interval);
+       pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n",
+                        cmd.snooze_window);
+
        return pos;
 }
 
@@ -654,6 +744,7 @@ const struct iwl_mvm_power_ops pm_mac_ops = {
        .power_update_mode = iwl_mvm_power_mac_update_mode,
        .power_update_device_mode = iwl_mvm_power_update_device,
        .power_disable = iwl_mvm_power_mac_disable,
+       .power_update_binding = _iwl_mvm_power_update_binding,
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
 #endif
index bf6e29f..d6d28d7 100644 (file)
 
 #define RS_NAME "iwl-mvm-rs"
 
-#define NUM_TRY_BEFORE_ANT_TOGGLE 1
-#define IWL_NUMBER_TRY      1
-#define IWL_HT_NUMBER_TRY   3
+#define NUM_TRY_BEFORE_ANT_TOGGLE       1
+#define RS_LEGACY_RETRIES_PER_RATE      1
+#define RS_HT_VHT_RETRIES_PER_RATE      2
+#define RS_HT_VHT_RETRIES_PER_RATE_TW   1
+#define RS_INITIAL_MIMO_NUM_RATES       3
+#define RS_INITIAL_SISO_NUM_RATES       3
+#define RS_INITIAL_LEGACY_NUM_RATES     LINK_QUAL_MAX_RETRY_NUM
+#define RS_SECONDARY_LEGACY_NUM_RATES   LINK_QUAL_MAX_RETRY_NUM
+#define RS_SECONDARY_SISO_NUM_RATES     3
+#define RS_SECONDARY_SISO_RETRIES       1
 
 #define IWL_RATE_MAX_WINDOW            62      /* # tx in history window */
 #define IWL_RATE_MIN_FAILURE_TH                3       /* min failures to calc tpt */
@@ -123,6 +130,12 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
        IWL_DECLARE_MCS_RATE(9),                 /* MCS 9 */
 };
 
+enum rs_action {
+       RS_ACTION_STAY = 0,
+       RS_ACTION_DOWNSCALE = -1,
+       RS_ACTION_UPSCALE = 1,
+};
+
 enum rs_column_mode {
        RS_INVALID = 0,
        RS_LEGACY,
@@ -351,20 +364,12 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                                   struct sk_buff *skb,
                                   struct ieee80211_sta *sta,
                                   struct iwl_lq_sta *lq_sta);
-static void rs_fill_link_cmd(struct iwl_mvm *mvm,
-                            struct ieee80211_sta *sta,
-                            struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
+static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
+                          struct ieee80211_sta *sta,
+                          struct iwl_lq_sta *lq_sta,
+                          const struct rs_rate *initial_rate);
 static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags);
-#else
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags)
-{}
-#endif
-
 /**
  * The following tables contain the expected throughput metrics for all rates
  *
@@ -504,30 +509,6 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
        return (ant_type & valid_antenna) == ant_type;
 }
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-/**
- * Program the device to use fixed rate for frame transmit
- * This is for debugging/testing only
- * once the device start use fixed rate, we need to reload the module
- * to being back the normal operation.
- */
-static void rs_program_fix_rate(struct iwl_mvm *mvm,
-                               struct iwl_lq_sta *lq_sta)
-{
-       lq_sta->active_legacy_rate = 0x0FFF;    /* 1 - 54 MBits, includes CCK */
-       lq_sta->active_siso_rate   = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
-       lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
-
-       IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
-                      lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
-
-       if (lq_sta->dbg_fixed_rate) {
-               rs_fill_link_cmd(NULL, NULL, lq_sta, lq_sta->dbg_fixed_rate);
-               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
-       }
-}
-#endif
-
 static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
                                      struct iwl_lq_sta *lq_data, u8 tid,
                                      struct ieee80211_sta *sta)
@@ -658,7 +639,7 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
 
 /* Convert rs_rate object into ucode rate bitmask */
 static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm,
-                                  struct rs_rate *rate)
+                                 struct rs_rate *rate)
 {
        u32 ucode_rate = 0;
        int index = rate->index;
@@ -785,8 +766,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,
 
 /* switch to another antenna/antennas and return 1 */
 /* if no other valid antenna found, return 0 */
-static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate,
-                            struct rs_rate *rate)
+static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate)
 {
        u8 new_ant_type;
 
@@ -807,9 +787,6 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate,
 
        rate->ant = new_ant_type;
 
-       /* TODO: get rid of ucode_rate here. This should handle only rs_rate */
-       *ucode_rate &= ~RATE_MCS_ANT_ABC_MSK;
-       *ucode_rate |= new_ant_type << RATE_MCS_ANT_POS;
        return 1;
 }
 
@@ -883,65 +860,73 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
        return (high << 8) | low;
 }
 
-static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
-                            struct rs_rate *rate,
-                            u8 scale_index, u8 ht_possible)
+static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta,
+                                    struct rs_rate *rate)
 {
-       s32 low;
-       u16 rate_mask;
+       return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate);
+}
+
+/* Get the next supported lower rate in the current column.
+ * Return true if bottom rate in the current column was reached
+ */
+static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
+                                       struct rs_rate *rate)
+{
+       u8 low;
        u16 high_low;
-       u8 switch_to_legacy = 0;
+       u16 rate_mask;
        struct iwl_mvm *mvm = lq_sta->drv;
 
-       /* check if we need to switch from HT to legacy rates.
-        * assumption is that mandatory rates (1Mbps or 6Mbps)
-        * are always supported (spec demand) */
-       if (!is_legacy(rate) && (!ht_possible || !scale_index)) {
-               switch_to_legacy = 1;
-               WARN_ON_ONCE(scale_index < IWL_RATE_MCS_0_INDEX &&
-                            scale_index > IWL_RATE_MCS_9_INDEX);
-               scale_index = rs_ht_to_legacy[scale_index];
+       rate_mask = rs_get_supported_rates(lq_sta, rate);
+       high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
+                                       rate->type);
+       low = high_low & 0xff;
+
+       /* Bottom rate of column reached */
+       if (low == IWL_RATE_INVALID)
+               return true;
+
+       rate->index = low;
+       return false;
+}
+
+/* Get the next rate to use following a column downgrade */
+static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
+                                         struct rs_rate *rate)
+{
+       struct iwl_mvm *mvm = lq_sta->drv;
+
+       if (is_legacy(rate)) {
+               /* No column to downgrade from Legacy */
+               return;
+       } else if (is_siso(rate)) {
+               /* Downgrade to Legacy if we were in SISO */
                if (lq_sta->band == IEEE80211_BAND_5GHZ)
                        rate->type = LQ_LEGACY_A;
                else
                        rate->type = LQ_LEGACY_G;
 
-               if (num_of_ant(rate->ant) > 1)
-                       rate->ant =
-                           first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
-
                rate->bw = RATE_MCS_CHAN_WIDTH_20;
-               rate->sgi = false;
-       }
 
-       rate_mask = rs_get_supported_rates(lq_sta, rate);
+               WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX &&
+                            rate->index > IWL_RATE_MCS_9_INDEX);
 
-       /* Mask with station rate restriction */
-       if (is_legacy(rate)) {
-               /* supp_rates has no CCK bits in A mode */
-               if (lq_sta->band == IEEE80211_BAND_5GHZ)
-                       rate_mask = (u16)(rate_mask &
-                          (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
-               else
-                       rate_mask = (u16)(rate_mask & lq_sta->supp_rates);
+               rate->index = rs_ht_to_legacy[rate->index];
+       } else {
+               /* Downgrade to SISO with same MCS if in MIMO  */
+               rate->type = is_vht_mimo2(rate) ?
+                       LQ_VHT_SISO : LQ_HT_SISO;
        }
 
-       /* If we switched from HT to legacy, check current rate */
-       if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
-               low = scale_index;
-               goto out;
-       }
 
-       high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
-                                       rate->type);
-       low = high_low & 0xff;
+       if (num_of_ant(rate->ant) > 1)
+               rate->ant = first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
 
-       if (low == IWL_RATE_INVALID)
-               low = scale_index;
+       /* Relevant in both switching to SISO or Legacy */
+       rate->sgi = false;
 
-out:
-       rate->index = low;
-       return ucode_rate_from_rs_rate(lq_sta->drv, rate);
+       if (!rs_rate_supported(lq_sta, rate))
+               rs_get_lower_rate_in_column(lq_sta, rate);
 }
 
 /* Simple function to compare two rate scale table types */
@@ -1137,14 +1122,9 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                                tmp_tbl = curr_tbl;
                        else if (rs_rate_match(&rate, &other_tbl->rate))
                                tmp_tbl = other_tbl;
-                       else {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Tx packet rate doesn't match ACTIVE or SEARCH tables\n");
-                               rs_dump_rate(mvm, &rate, "Tx PACKET:");
-                               rs_dump_rate(mvm, &curr_tbl->rate, "CURRENT:");
-                               rs_dump_rate(mvm, &other_tbl->rate, "OTHER:");
+                       else
                                continue;
-                       }
+
                        rs_collect_tx_data(tmp_tbl, rate.index, 1,
                                           i < retries ? 0 : legacy_success);
                }
@@ -1471,10 +1451,7 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm,
                               struct iwl_lq_sta *lq_sta,
                               struct rs_rate *rate)
 {
-       u32 ucode_rate;
-
-       ucode_rate = ucode_rate_from_rs_rate(mvm, rate);
-       rs_fill_link_cmd(mvm, sta, lq_sta, ucode_rate);
+       rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
 }
 
@@ -1634,10 +1611,6 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
                rate->index = rate_idx;
        }
 
-       /* TODO: remove current_rate and keep using rs_rate all the way until
-        * we need to fill in the rs_table in the LQ command
-        */
-       search_tbl->current_rate = ucode_rate_from_rs_rate(mvm, rate);
        IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n",
                       col_id, rate->index);
 
@@ -1649,6 +1622,97 @@ err:
        return -1;
 }
 
+static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
+                                        struct iwl_scale_tbl_info *tbl,
+                                        s32 sr, int low, int high,
+                                        int current_tpt,
+                                        int low_tpt, int high_tpt)
+{
+       enum rs_action action = RS_ACTION_STAY;
+
+       /* Too many failures, decrease rate */
+       if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
+               IWL_DEBUG_RATE(mvm,
+                              "decrease rate because of low SR\n");
+               action = RS_ACTION_DOWNSCALE;
+       /* No throughput measured yet for adjacent rates; try increase. */
+       } else if ((low_tpt == IWL_INVALID_VALUE) &&
+                  (high_tpt == IWL_INVALID_VALUE)) {
+               if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "Good SR and no high rate measurement. "
+                                      "Increase rate\n");
+                       action = RS_ACTION_UPSCALE;
+               } else if (low != IWL_RATE_INVALID) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "Remain in current rate\n");
+                       action = RS_ACTION_STAY;
+               }
+       }
+
+       /* Both adjacent throughputs are measured, but neither one has better
+        * throughput; we're using the best rate, don't change it!
+        */
+       else if ((low_tpt != IWL_INVALID_VALUE) &&
+                (high_tpt != IWL_INVALID_VALUE) &&
+                (low_tpt < current_tpt) &&
+                (high_tpt < current_tpt)) {
+               IWL_DEBUG_RATE(mvm,
+                              "Both high and low are worse. "
+                              "Maintain rate\n");
+               action = RS_ACTION_STAY;
+       }
+
+       /* At least one adjacent rate's throughput is measured,
+        * and may have better performance.
+        */
+       else {
+               /* Higher adjacent rate's throughput is measured */
+               if (high_tpt != IWL_INVALID_VALUE) {
+                       /* Higher rate has better throughput */
+                       if (high_tpt > current_tpt &&
+                           sr >= IWL_RATE_INCREASE_TH) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Higher rate is better and good "
+                                              "SR. Increate rate\n");
+                               action = RS_ACTION_UPSCALE;
+                       } else {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Higher rate isn't better OR "
+                                              "no good SR. Maintain rate\n");
+                               action = RS_ACTION_STAY;
+                       }
+
+               /* Lower adjacent rate's throughput is measured */
+               } else if (low_tpt != IWL_INVALID_VALUE) {
+                       /* Lower rate has better throughput */
+                       if (low_tpt > current_tpt) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Lower rate is better. "
+                                              "Decrease rate\n");
+                               action = RS_ACTION_DOWNSCALE;
+                       } else if (sr >= IWL_RATE_INCREASE_TH) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Lower rate isn't better and "
+                                              "good SR. Increase rate\n");
+                               action = RS_ACTION_UPSCALE;
+                       }
+               }
+       }
+
+       /* Sanity check; asked for decrease, but success rate or throughput
+        * has been good at old rate.  Don't change it.
+        */
+       if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID) &&
+           ((sr > IWL_RATE_HIGH_TH) ||
+            (current_tpt > (100 * tbl->expected_tpt[low])))) {
+               IWL_DEBUG_RATE(mvm,
+                              "Sanity check failed. Maintain rate\n");
+               action = RS_ACTION_STAY;
+       }
+
+       return action;
+}
 
 /*
  * Do rate scaling and search for new modulation mode.
@@ -1669,11 +1733,10 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        int low_tpt = IWL_INVALID_VALUE;
        int high_tpt = IWL_INVALID_VALUE;
        u32 fail_count;
-       s8 scale_action = 0;
+       enum rs_action scale_action = RS_ACTION_STAY;
        u16 rate_mask;
        u8 update_lq = 0;
        struct iwl_scale_tbl_info *tbl, *tbl1;
-       u16 rate_scale_index_msk = 0;
        u8 active_tbl = 0;
        u8 done_search = 0;
        u16 high_low;
@@ -1690,8 +1753,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
            info->flags & IEEE80211_TX_CTL_NO_ACK)
                return;
 
-       lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
-
        tid = rs_get_tid(lq_sta, hdr);
        if ((tid != IWL_MAX_TID_COUNT) &&
            (lq_sta->tx_agg_tid_en & (1 << tid))) {
@@ -1730,33 +1791,13 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        /* rates available for this association, and for modulation mode */
        rate_mask = rs_get_supported_rates(lq_sta, rate);
 
-       /* mask with station rate restriction */
-       if (is_legacy(rate)) {
-               if (lq_sta->band == IEEE80211_BAND_5GHZ)
-                       /* supp_rates has no CCK bits in A mode */
-                       rate_scale_index_msk = (u16) (rate_mask &
-                               (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
-               else
-                       rate_scale_index_msk = (u16) (rate_mask &
-                                                     lq_sta->supp_rates);
-
-       } else {
-               rate_scale_index_msk = rate_mask;
-       }
-
-       if (!rate_scale_index_msk)
-               rate_scale_index_msk = rate_mask;
-
-       if (!((BIT(index) & rate_scale_index_msk))) {
+       if (!(BIT(index) & rate_mask)) {
                IWL_ERR(mvm, "Current Rate is not valid\n");
                if (lq_sta->search_better_tbl) {
                        /* revert to active table if search table is not valid*/
                        rate->type = LQ_NONE;
                        lq_sta->search_better_tbl = 0;
                        tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-                       /* get "active" rate info */
-                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
-                       tbl->rate.index = index;
                        rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
                }
                return;
@@ -1847,7 +1888,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                        tbl = &(lq_sta->lq_info[active_tbl]);
 
                        /* Revert to "active" rate and throughput info */
-                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       index = tbl->rate.index;
                        current_tpt = lq_sta->last_tpt;
 
                        /* Need to set up a new rate table in uCode */
@@ -1863,8 +1904,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
 
        /* (Else) not in search of better modulation mode, try for better
         * starting rate, while staying in this mode. */
-       high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk,
-                                       rate->type);
+       high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type);
        low = high_low & 0xff;
        high = (high_low >> 8) & 0xff;
 
@@ -1887,85 +1927,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                       rs_pretty_lq_type(rate->type), index, current_tpt, sr,
                       low, high, low_tpt, high_tpt);
 
-       scale_action = 0;
-
-       /* Too many failures, decrease rate */
-       if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
-               IWL_DEBUG_RATE(mvm,
-                              "decrease rate because of low SR\n");
-               scale_action = -1;
-       /* No throughput measured yet for adjacent rates; try increase. */
-       } else if ((low_tpt == IWL_INVALID_VALUE) &&
-                  (high_tpt == IWL_INVALID_VALUE)) {
-               if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) {
-                       IWL_DEBUG_RATE(mvm,
-                                      "Good SR and no high rate measurement. "
-                                      "Increase rate\n");
-                       scale_action = 1;
-               } else if (low != IWL_RATE_INVALID) {
-                       IWL_DEBUG_RATE(mvm,
-                                      "Remain in current rate\n");
-                       scale_action = 0;
-               }
-       }
-
-       /* Both adjacent throughputs are measured, but neither one has better
-        * throughput; we're using the best rate, don't change it! */
-       else if ((low_tpt != IWL_INVALID_VALUE) &&
-                (high_tpt != IWL_INVALID_VALUE) &&
-                (low_tpt < current_tpt) &&
-                (high_tpt < current_tpt)) {
-               IWL_DEBUG_RATE(mvm,
-                              "Both high and low are worse. "
-                              "Maintain rate\n");
-               scale_action = 0;
-       }
-
-       /* At least one adjacent rate's throughput is measured,
-        * and may have better performance. */
-       else {
-               /* Higher adjacent rate's throughput is measured */
-               if (high_tpt != IWL_INVALID_VALUE) {
-                       /* Higher rate has better throughput */
-                       if (high_tpt > current_tpt &&
-                           sr >= IWL_RATE_INCREASE_TH) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Higher rate is better and good "
-                                              "SR. Increate rate\n");
-                               scale_action = 1;
-                       } else {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Higher rate isn't better OR "
-                                              "no good SR. Maintain rate\n");
-                               scale_action = 0;
-                       }
-
-               /* Lower adjacent rate's throughput is measured */
-               } else if (low_tpt != IWL_INVALID_VALUE) {
-                       /* Lower rate has better throughput */
-                       if (low_tpt > current_tpt) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Lower rate is better. "
-                                              "Decrease rate\n");
-                               scale_action = -1;
-                       } else if (sr >= IWL_RATE_INCREASE_TH) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Lower rate isn't better and "
-                                              "good SR. Increase rate\n");
-                               scale_action = 1;
-                       }
-               }
-       }
-
-       /* Sanity check; asked for decrease, but success rate or throughput
-        * has been good at old rate.  Don't change it. */
-       if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
-           ((sr > IWL_RATE_HIGH_TH) ||
-            (current_tpt > (100 * tbl->expected_tpt[low])))) {
-               IWL_DEBUG_RATE(mvm,
-                              "Sanity check failed. Maintain rate\n");
-               scale_action = 0;
-       }
+       scale_action = rs_get_rate_action(mvm, tbl, sr, low, high,
+                                         current_tpt, low_tpt, high_tpt);
 
        /* Force a search in case BT doesn't like us being in MIMO */
        if (is_mimo(rate) &&
@@ -1977,7 +1940,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        }
 
        switch (scale_action) {
-       case -1:
+       case RS_ACTION_DOWNSCALE:
                /* Decrease starting rate, update uCode's rate table */
                if (low != IWL_RATE_INVALID) {
                        update_lq = 1;
@@ -1988,7 +1951,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                }
 
                break;
-       case 1:
+       case RS_ACTION_UPSCALE:
                /* Increase starting rate, update uCode's rate table */
                if (high != IWL_RATE_INVALID) {
                        update_lq = 1;
@@ -1999,7 +1962,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                }
 
                break;
-       case 0:
+       case RS_ACTION_STAY:
                /* No change */
        default:
                break;
@@ -2053,11 +2016,11 @@ lq_update:
                                rs_rate_scale_clear_window(&(tbl->win[i]));
 
                        /* Use new "search" start rate */
-                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       index = tbl->rate.index;
 
                        rs_dump_rate(mvm, &tbl->rate,
                                     "Switch to SEARCH TABLE:");
-                       rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate);
+                       rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
                        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
                } else {
                        done_search = 1;
@@ -2095,8 +2058,6 @@ lq_update:
        }
 
 out:
-       tbl->rate.index = index;
-       tbl->current_rate = ucode_rate_from_rs_rate(mvm, &tbl->rate);
        lq_sta->last_txrate_idx = index;
 }
 
@@ -2123,7 +2084,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        struct iwl_scale_tbl_info *tbl;
        struct rs_rate *rate;
        int i;
-       u32 ucode_rate;
        u8 active_tbl = 0;
        u8 valid_tx_ant;
 
@@ -2154,9 +2114,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        else
                rate->type = LQ_LEGACY_G;
 
-       ucode_rate = ucode_rate_from_rs_rate(mvm, rate);
-       tbl->current_rate = ucode_rate;
-
        WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B);
        if (rate->ant == ANT_A)
                tbl->column = RS_COLUMN_LEGACY_ANT_A;
@@ -2164,7 +2121,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
                tbl->column = RS_COLUMN_LEGACY_ANT_B;
 
        rs_set_expected_tpt_table(lq_sta, tbl);
-       rs_fill_link_cmd(NULL, NULL, lq_sta, ucode_rate);
+       rs_fill_lq_cmd(NULL, NULL, lq_sta, rate);
        /* TODO restore station should remember the lq cmd */
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init);
 }
@@ -2250,6 +2207,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
+                       /* Disable MCS9 as a workaround */
+                       if (i == IWL_RATE_MCS_9_INDEX)
+                               continue;
+
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2268,6 +2229,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
+                       /* Disable MCS9 as a workaround */
+                       if (i == IWL_RATE_MCS_9_INDEX)
+                               continue;
+
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2306,7 +2271,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                        rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
        lq_sta->flush_timer = 0;
-       lq_sta->supp_rates = sta->supp_rates[sband->band];
 
        IWL_DEBUG_RATE(mvm,
                       "LQ: *** rate scale station global init for station %d ***\n",
@@ -2395,112 +2359,164 @@ static void rs_rate_update(void *mvm_r,
        iwl_mvm_rs_rate_init(mvm, sta, sband->band, false);
 }
 
-static void rs_fill_link_cmd(struct iwl_mvm *mvm,
-                            struct ieee80211_sta *sta,
-                            struct iwl_lq_sta *lq_sta, u32 new_rate)
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
+                                           struct iwl_lq_cmd *lq_cmd,
+                                           enum ieee80211_band band,
+                                           u32 ucode_rate)
 {
        struct rs_rate rate;
-       int index = 0;
-       int repeat_rate = 0;
-       u8 ant_toggle_cnt = 0;
-       u8 use_ht_possible = 1;
-       u8 valid_tx_ant = 0;
-       struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+       int i;
+       int num_rates = ARRAY_SIZE(lq_cmd->rs_table);
+       __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate);
 
-       /* Override starting rate (index 0) if needed for debug purposes */
-       rs_dbgfs_set_mcs(lq_sta, &new_rate);
+       for (i = 0; i < num_rates; i++)
+               lq_cmd->rs_table[i] = ucode_rate_le32;
 
-       rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate);
+       rs_rate_from_ucode_rate(ucode_rate, band, &rate);
 
-       /* How many times should we repeat the initial rate? */
-       if (is_legacy(&rate)) {
-               ant_toggle_cnt = 1;
-               repeat_rate = IWL_NUMBER_TRY;
-       } else {
-               repeat_rate = min(IWL_HT_NUMBER_TRY,
-                                 LINK_QUAL_AGG_DISABLE_START_DEF - 1);
+       if (is_mimo(&rate))
+               lq_cmd->mimo_delim = num_rates - 1;
+       else
+               lq_cmd->mimo_delim = 0;
+}
+#endif /* CONFIG_MAC80211_DEBUGFS */
+
+static void rs_fill_rates_for_column(struct iwl_mvm *mvm,
+                                    struct iwl_lq_sta *lq_sta,
+                                    struct rs_rate *rate,
+                                    __le32 *rs_table, int *rs_table_index,
+                                    int num_rates, int num_retries,
+                                    u8 valid_tx_ant, bool toggle_ant)
+{
+       int i, j;
+       __le32 ucode_rate;
+       bool bottom_reached = false;
+       int prev_rate_idx = rate->index;
+       int end = LINK_QUAL_MAX_RETRY_NUM;
+       int index = *rs_table_index;
+
+       for (i = 0; i < num_rates && index < end; i++) {
+               ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, rate));
+               for (j = 0; j < num_retries && index < end; j++, index++)
+                       rs_table[index] = ucode_rate;
+
+               if (toggle_ant)
+                       rs_toggle_antenna(valid_tx_ant, rate);
+
+               prev_rate_idx = rate->index;
+               bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate);
+               if (bottom_reached && !is_legacy(rate))
+                       break;
        }
 
-       lq_cmd->mimo_delim = is_mimo(&rate) ? 1 : 0;
+       if (!bottom_reached)
+               rate->index = prev_rate_idx;
 
-       /* Fill 1st table entry (index 0) */
-       lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
+       *rs_table_index = index;
+}
 
-       if (num_of_ant(rate.ant) == 1)
-               lq_cmd->single_stream_ant_msk = rate.ant;
-       /* otherwise we don't modify the existing value */
+/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI
+ * column the rate table should look like this:
+ *
+ * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI
+ * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI
+ * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI
+ * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps
+ * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps
+ * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps
+ * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps
+ * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps
+ * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps
+ * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps
+ */
+static void rs_build_rates_table(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                const struct rs_rate *initial_rate)
+{
+       struct rs_rate rate;
+       int num_rates, num_retries, index = 0;
+       u8 valid_tx_ant = 0;
+       struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+       bool toggle_ant = false;
+
+       memcpy(&rate, initial_rate, sizeof(struct rs_rate));
 
-       index++;
-       repeat_rate--;
        if (mvm)
                valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
 
-       /* Fill rest of rate table */
-       while (index < LINK_QUAL_MAX_RETRY_NUM) {
-               /* Repeat initial/next rate.
-                * For legacy IWL_NUMBER_TRY == 1, this loop will not execute.
-                * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */
-               while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) {
-                       if (is_legacy(&rate)) {
-                               if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
-                                       ant_toggle_cnt++;
-                               else if (mvm &&
-                                        rs_toggle_antenna(valid_tx_ant,
-                                                       &new_rate, &rate))
-                                       ant_toggle_cnt = 1;
-                       }
+       if (is_siso(&rate)) {
+               num_rates = RS_INITIAL_SISO_NUM_RATES;
+               num_retries = RS_HT_VHT_RETRIES_PER_RATE;
+       } else if (is_mimo(&rate)) {
+               num_rates = RS_INITIAL_MIMO_NUM_RATES;
+               num_retries = RS_HT_VHT_RETRIES_PER_RATE;
+       } else {
+               num_rates = RS_INITIAL_LEGACY_NUM_RATES;
+               num_retries = RS_LEGACY_RETRIES_PER_RATE;
+               toggle_ant = true;
+       }
 
-                       /* Override next rate if needed for debug purposes */
-                       rs_dbgfs_set_mcs(lq_sta, &new_rate);
+       rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+                                num_rates, num_retries, valid_tx_ant,
+                                toggle_ant);
 
-                       /* Fill next table entry */
-                       lq_cmd->rs_table[index] =
-                                       cpu_to_le32(new_rate);
-                       repeat_rate--;
-                       index++;
-               }
+       rs_get_lower_rate_down_column(lq_sta, &rate);
 
-               rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate);
+       if (is_siso(&rate)) {
+               num_rates = RS_SECONDARY_SISO_NUM_RATES;
+               num_retries = RS_SECONDARY_SISO_RETRIES;
+       } else if (is_legacy(&rate)) {
+               num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
+               num_retries = RS_LEGACY_RETRIES_PER_RATE;
+       } else {
+               WARN_ON_ONCE(1);
+       }
 
-               /* Indicate to uCode which entries might be MIMO.
-                * If initial rate was MIMO, this will finally end up
-                * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
-               if (is_mimo(&rate))
-                       lq_cmd->mimo_delim = index;
+       toggle_ant = true;
 
-               /* Get next rate */
-               new_rate = rs_get_lower_rate(lq_sta, &rate, rate.index,
-                                            use_ht_possible);
+       rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+                                num_rates, num_retries, valid_tx_ant,
+                                toggle_ant);
 
-               /* How many times should we repeat the next rate? */
-               if (is_legacy(&rate)) {
-                       if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
-                               ant_toggle_cnt++;
-                       else if (mvm &&
-                                rs_toggle_antenna(valid_tx_ant,
-                                                  &new_rate, &rate))
-                               ant_toggle_cnt = 1;
+       rs_get_lower_rate_down_column(lq_sta, &rate);
 
-                       repeat_rate = IWL_NUMBER_TRY;
-               } else {
-                       repeat_rate = IWL_HT_NUMBER_TRY;
-               }
+       num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
+       num_retries = RS_LEGACY_RETRIES_PER_RATE;
 
-               /* Don't allow HT rates after next pass.
-                * rs_get_lower_rate() will change type to LQ_LEGACY_A
-                * or LQ_LEGACY_G.
-                */
-               use_ht_possible = 0;
+       rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+                                num_rates, num_retries, valid_tx_ant,
+                                toggle_ant);
 
-               /* Override next rate if needed for debug purposes */
-               rs_dbgfs_set_mcs(lq_sta, &new_rate);
+}
 
-               /* Fill next table entry */
-               lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
+static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
+                          struct ieee80211_sta *sta,
+                          struct iwl_lq_sta *lq_sta,
+                          const struct rs_rate *initial_rate)
+{
+       struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+       u8 ant = initial_rate->ant;
 
-               index++;
-               repeat_rate--;
-       }
+#ifdef CONFIG_MAC80211_DEBUGFS
+       if (lq_sta->dbg_fixed_rate) {
+               rs_build_rates_table_from_fixed(mvm, lq_cmd,
+                                               lq_sta->band,
+                                               lq_sta->dbg_fixed_rate);
+               ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
+                       RATE_MCS_ANT_POS;
+       } else
+#endif
+               rs_build_rates_table(mvm, lq_sta, initial_rate);
+
+       if (num_of_ant(ant) == 1)
+               lq_cmd->single_stream_ant_msk = ant;
 
        lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
        lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
@@ -2534,31 +2550,6 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
 }
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags)
-{
-       struct iwl_mvm *mvm;
-       u8 valid_tx_ant;
-       u8 ant_sel_tx;
-
-       mvm = lq_sta->drv;
-       valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
-       if (lq_sta->dbg_fixed_rate) {
-               ant_sel_tx =
-                 ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
-                 >> RATE_MCS_ANT_POS);
-               if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) {
-                       *rate_n_flags = lq_sta->dbg_fixed_rate;
-               } else {
-                       lq_sta->dbg_fixed_rate = 0;
-                       IWL_ERR(mvm,
-                               "Invalid antenna selection 0x%X, Valid is 0x%X\n",
-                               ant_sel_tx, valid_tx_ant);
-                       IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n");
-               }
-       }
-}
-
 static int rs_pretty_print_rate(char *buf, const u32 rate)
 {
 
@@ -2612,6 +2603,31 @@ static int rs_pretty_print_rate(char *buf, const u32 rate)
                       (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : "");
 }
 
+/**
+ * Program the device to use fixed rate for frame transmit
+ * This is for debugging/testing only
+ * once the device start use fixed rate, we need to reload the module
+ * to being back the normal operation.
+ */
+static void rs_program_fix_rate(struct iwl_mvm *mvm,
+                               struct iwl_lq_sta *lq_sta)
+{
+       lq_sta->active_legacy_rate = 0x0FFF;    /* 1 - 54 MBits, includes CCK */
+       lq_sta->active_siso_rate   = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+       lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+
+       IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
+                      lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
+
+       if (lq_sta->dbg_fixed_rate) {
+               struct rs_rate rate;
+               rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate,
+                                       lq_sta->band, &rate);
+               rs_fill_lq_cmd(NULL, NULL, lq_sta, &rate);
+               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
+       }
+}
+
 static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
                        const char __user *user_buf, size_t count, loff_t *ppos)
 {
@@ -2702,12 +2718,10 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                        lq_sta->lq.initial_rate_index[3]);
 
        for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
-               u32 rate = le32_to_cpu(lq_sta->lq.rs_table[i]);
-               desc += sprintf(buff+desc,
-                               " rate[%d] 0x%X ",
-                               i, rate);
+               u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]);
 
-               desc += rs_pretty_print_rate(buff+desc, rate);
+               desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r);
+               desc += rs_pretty_print_rate(buff+desc, r);
        }
 
        ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
@@ -2741,14 +2755,14 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
                rate = &tbl->rate;
                desc += sprintf(buff+desc,
                                "%s type=%d SGI=%d BW=%s DUP=0\n"
-                               "rate=0x%X\n",
+                               "index=%d\n",
                                lq_sta->active_tbl == i ? "*" : "x",
                                rate->type,
                                rate->sgi,
                                is_ht20(rate) ? "20Mhz" :
                                is_ht40(rate) ? "40Mhz" :
                                is_ht80(rate) ? "80Mhz" : "ERR",
-                               tbl->current_rate);
+                               rate->index);
                for (j = 0; j < IWL_RATE_COUNT; j++) {
                        desc += sprintf(buff+desc,
                                "counter=%d success=%d %%=%d\n",
index b329607..c31aa59 100644 (file)
@@ -278,7 +278,6 @@ struct iwl_scale_tbl_info {
        struct rs_rate rate;
        enum rs_column column;
        s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
-       u32 current_rate;  /* rate_n_flags, uCode API format */
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
 };
 
@@ -315,7 +314,6 @@ struct iwl_lq_sta {
        enum ieee80211_band band;
 
        /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
-       u32 supp_rates;
        u16 active_legacy_rate;
        u16 active_siso_rate;
        u16 active_mimo2_rate;
index 3a1f398..454341c 100644 (file)
@@ -251,6 +251,12 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
                stats->flag |= RX_FLAG_DECRYPTED;
                return 0;
 
+       case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
+                       return -1;
+               stats->flag |= RX_FLAG_DECRYPTED;
+               return 0;
+
        default:
                IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
        }
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
new file mode 100644 (file)
index 0000000..97bb3c3
--- /dev/null
@@ -0,0 +1,291 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 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 COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "mvm.h"
+
+/* For counting bound interfaces */
+struct iwl_mvm_active_iface_iterator_data {
+       struct ieee80211_vif *ignore_vif;
+       u8 sta_vif_ap_sta_id;
+       enum iwl_sf_state sta_vif_state;
+       int num_active_macs;
+};
+
+/*
+ * Count bound interfaces which are not p2p, besides data->ignore_vif.
+ * data->station_vif will point to one bound vif of type station, if exists.
+ */
+static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_active_iface_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
+           vif->type == NL80211_IFTYPE_P2P_DEVICE)
+               return;
+
+       data->num_active_macs++;
+
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
+               if (vif->bss_conf.assoc)
+                       data->sta_vif_state = SF_FULL_ON;
+               else
+                       data->sta_vif_state = SF_INIT_OFF;
+       }
+}
+
+/*
+ * Aging and idle timeouts for the different possible scenarios
+ * in SF_FULL_ON state.
+ */
+static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
+       {
+               cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
+               cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
+               cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_MCAST_AGING_TIMER),
+               cpu_to_le32(SF_MCAST_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_BA_AGING_TIMER),
+               cpu_to_le32(SF_BA_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_TX_RE_AGING_TIMER),
+               cpu_to_le32(SF_TX_RE_IDLE_TIMER)
+       },
+};
+
+static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
+                                   struct ieee80211_sta *sta)
+{
+       int i, j, watermark;
+
+       sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
+
+       /*
+        * If we are in association flow - check antenna configuration
+        * capabilities of the AP station, and choose the watermark accordingly.
+        */
+       if (sta) {
+               if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
+                       switch (sta->rx_nss) {
+                       case 1:
+                               watermark = SF_W_MARK_SISO;
+                               break;
+                       case 2:
+                               watermark = SF_W_MARK_MIMO2;
+                               break;
+                       default:
+                               watermark = SF_W_MARK_MIMO3;
+                               break;
+                       }
+               } else {
+                       watermark = SF_W_MARK_LEGACY;
+               }
+       /* default watermark value for unassociated mode. */
+       } else {
+               watermark = SF_W_MARK_MIMO2;
+       }
+       sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
+
+       for (i = 0; i < SF_NUM_SCENARIO; i++) {
+               for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
+                       sf_cmd->long_delay_timeouts[i][j] =
+                                       cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
+               }
+       }
+       BUILD_BUG_ON(sizeof(sf_full_timeout) !=
+                    sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
+
+       memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
+              sizeof(sf_full_timeout));
+}
+
+static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
+                            enum iwl_sf_state new_state)
+{
+       struct iwl_sf_cfg_cmd sf_cmd = {
+               .state = new_state,
+       };
+       struct ieee80211_sta *sta;
+       int ret = 0;
+
+       /*
+        * If an associated AP sta changed its antenna configuration, the state
+        * will remain FULL_ON but SF parameters need to be reconsidered.
+        */
+       if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
+               return 0;
+
+       switch (new_state) {
+       case SF_UNINIT:
+               break;
+       case SF_FULL_ON:
+               if (sta_id == IWL_MVM_STATION_COUNT) {
+                       IWL_ERR(mvm,
+                               "No station: Cannot switch SF to FULL_ON\n");
+                       return -EINVAL;
+               }
+               rcu_read_lock();
+               sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+               if (IS_ERR_OR_NULL(sta)) {
+                       IWL_ERR(mvm, "Invalid station id\n");
+                       rcu_read_unlock();
+                       return -EINVAL;
+               }
+               iwl_mvm_fill_sf_command(&sf_cmd, sta);
+               rcu_read_unlock();
+               break;
+       case SF_INIT_OFF:
+               iwl_mvm_fill_sf_command(&sf_cmd, NULL);
+               break;
+       default:
+               WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
+                         new_state);
+               return -EINVAL;
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
+                                  sizeof(sf_cmd), &sf_cmd);
+       if (!ret)
+               mvm->sf_state = new_state;
+
+       return ret;
+}
+
+/*
+ * Update Smart fifo:
+ * Count bound interfaces that are not to be removed, ignoring p2p devices,
+ * and set new state accordingly.
+ */
+int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
+                     bool remove_vif)
+{
+       enum iwl_sf_state new_state;
+       u8 sta_id = IWL_MVM_STATION_COUNT;
+       struct iwl_mvm_vif *mvmvif = NULL;
+       struct iwl_mvm_active_iface_iterator_data data = {
+               .ignore_vif = changed_vif,
+               .sta_vif_state = SF_UNINIT,
+               .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
+       };
+
+       if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
+               return 0;
+
+       /*
+        * Ignore the call if we are in HW Restart flow, or if the handled
+        * vif is a p2p device.
+        */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
+           (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
+               return 0;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_bound_iface_iterator,
+                                                  &data);
+
+       /* If changed_vif exists and is not to be removed, add to the count */
+       if (changed_vif && !remove_vif)
+               data.num_active_macs++;
+
+       switch (data.num_active_macs) {
+       case 0:
+               /* If there are no active macs - change state to SF_INIT_OFF */
+               new_state = SF_INIT_OFF;
+               break;
+       case 1:
+               if (remove_vif) {
+                       /* The one active mac left is of type station
+                        * and we filled the relevant data during iteration
+                        */
+                       new_state = data.sta_vif_state;
+                       sta_id = data.sta_vif_ap_sta_id;
+               } else {
+                       if (WARN_ON(!changed_vif))
+                               return -EINVAL;
+                       if (changed_vif->type != NL80211_IFTYPE_STATION) {
+                               new_state = SF_UNINIT;
+                       } else if (changed_vif->bss_conf.assoc) {
+                               mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
+                               sta_id = mvmvif->ap_sta_id;
+                               new_state = SF_FULL_ON;
+                       } else {
+                               new_state = SF_INIT_OFF;
+                       }
+               }
+               break;
+       default:
+               /* If there are multiple active macs - change to SF_UNINIT */
+               new_state = SF_UNINIT;
+       }
+       return iwl_mvm_sf_config(mvm, sta_id, new_state);
+}
index 026c2e0..0a8af20 100644 (file)
@@ -939,19 +939,6 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
                     sta->addr, tid);
 
-       if (mvm->cfg->ht_params->use_rts_for_aggregation) {
-               /*
-                * switch to RTS/CTS if it is the prefer protection
-                * method for HT traffic
-                * this function also sends the LQ command
-                */
-               return iwl_mvm_tx_protection(mvm, mvmsta, true);
-               /*
-                * TODO: remove the TLC_RTS flag when we tear down the last
-                * AGG session (agg_tids_count in DVM)
-                */
-       }
-
        return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false);
 }
 
@@ -1130,8 +1117,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
        default:
-               WARN_ON(1);
-               return -EINVAL;
+               key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
+               memcpy(cmd.key, keyconf->key, keyconf->keylen);
        }
 
        if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
@@ -1295,8 +1282,8 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                                           0, NULL, CMD_SYNC);
                break;
        default:
-               IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher);
-               ret = -EINVAL;
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
+                                          sta_id, 0, NULL, CMD_SYNC);
        }
 
        if (ret)
index 18be04d..a0ec7b3 100644 (file)
@@ -340,7 +340,7 @@ static void check_exit_ctkill(struct work_struct *work)
 
        iwl_trans_start_hw(mvm->trans);
        temp = check_nic_temperature(mvm);
-       iwl_trans_stop_hw(mvm->trans, false);
+       iwl_trans_stop_device(mvm->trans);
 
        if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
                IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
index d87649a..735f86d 100644 (file)
@@ -253,8 +253,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
                memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
                break;
        default:
-               IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher);
-               break;
+               tx_cmd->sec_ctl |= TX_CMD_SEC_EXT;
        }
 }
 
index 56cf819..f4aff56 100644 (file)
@@ -518,6 +518,11 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        int i;
 
        lockdep_assert_held(&mvm->mutex);
+
+       /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */
+       if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1)
+               return;
+
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
        mvmvif->smps_requests[req_type] = smps_request;
        for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
index 051268c..674c75b 100644 (file)
@@ -256,7 +256,6 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
  * @hw_base: pci hardware address support
  * @ucode_write_complete: indicates that the ucode has been copied.
  * @ucode_write_waitq: wait queue for uCode load
- * @status - transport specific status flags
  * @cmd_queue - command queue number
  * @rx_buf_size_8k: 8 kB RX buffer size
  * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
@@ -296,7 +295,6 @@ struct iwl_trans_pcie {
        wait_queue_head_t ucode_write_waitq;
        wait_queue_head_t wait_command_queue;
 
-       unsigned long status;
        u8 cmd_queue;
        u8 cmd_fifo;
        u8 n_no_reclaim_cmds;
@@ -315,24 +313,6 @@ struct iwl_trans_pcie {
        spinlock_t reg_lock;
 };
 
-/**
- * enum iwl_pcie_status: status of the PCIe transport
- * @STATUS_HCMD_ACTIVE: a SYNC command is being processed
- * @STATUS_DEVICE_ENABLED: APM is enabled
- * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
- * @STATUS_INT_ENABLED: interrupts are enabled
- * @STATUS_RFKILL: the HW RFkill switch is in KILL position
- * @STATUS_FW_ERROR: the fw is in error state
- */
-enum iwl_pcie_status {
-       STATUS_HCMD_ACTIVE,
-       STATUS_DEVICE_ENABLED,
-       STATUS_TPOWER_PMI,
-       STATUS_INT_ENABLED,
-       STATUS_RFKILL,
-       STATUS_FW_ERROR,
-};
-
 #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
        ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific))
 
@@ -399,8 +379,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans);
 ******************************************************/
 static inline void iwl_disable_interrupts(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       clear_bit(STATUS_INT_ENABLED, &trans_pcie->status);
+       clear_bit(STATUS_INT_ENABLED, &trans->status);
 
        /* disable interrupts from uCode/NIC to host */
        iwl_write32(trans, CSR_INT_MASK, 0x00000000);
@@ -417,7 +396,7 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
        IWL_DEBUG_ISR(trans, "Enabling interrupts\n");
-       set_bit(STATUS_INT_ENABLED, &trans_pcie->status);
+       set_bit(STATUS_INT_ENABLED, &trans->status);
        iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
 }
 
@@ -477,12 +456,4 @@ static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
                CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
 }
 
-static inline void iwl_nic_error(struct iwl_trans *trans)
-{
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
-       set_bit(STATUS_FW_ERROR, &trans_pcie->status);
-       iwl_op_mode_nic_error(trans->op_mode);
-}
-
 #endif /* __iwl_trans_int_pcie_h__ */
index 1d6bf7b..7aeec5c 100644 (file)
@@ -162,11 +162,8 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
                rxq->write_actual = (rxq->write & ~0x7);
                iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
        } else {
-               struct iwl_trans_pcie *trans_pcie =
-                       IWL_TRANS_GET_PCIE_TRANS(trans);
-
                /* If power-saving is in use, make sure device is awake */
-               if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) {
+               if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
                        reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
 
                        if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
@@ -222,7 +219,7 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
         * stopped, we cannot access the HW (in particular not prph).
         * So don't try to restock if the APM has been already stopped.
         */
-       if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status))
+       if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
                return;
 
        spin_lock_irqsave(&rxq->lock, flags);
@@ -791,7 +788,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
                             APMS_CLK_VAL_MRB_FUNC_MODE) ||
             (iwl_read_prph(trans, APMG_PS_CTRL_REG) &
                            APMG_PS_CTRL_VAL_RESET_REQ))) {
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                iwl_op_mode_wimax_active(trans->op_mode);
                wake_up(&trans_pcie->wait_command_queue);
                return;
@@ -800,14 +797,14 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
        iwl_pcie_dump_csr(trans);
        iwl_dump_fh(trans, NULL);
 
-       /* set the ERROR bit before we wake up the caller */
-       set_bit(STATUS_FW_ERROR, &trans_pcie->status);
-       clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
-       wake_up(&trans_pcie->wait_command_queue);
-
        local_bh_disable();
-       iwl_nic_error(trans);
+       /* The STATUS_FW_ERROR bit is set in this function. This must happen
+        * before we wake up the command caller, to ensure a proper cleanup. */
+       iwl_trans_fw_error(trans);
        local_bh_enable();
+
+       clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+       wake_up(&trans_pcie->wait_command_queue);
 }
 
 irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
@@ -894,14 +891,14 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
                iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
                if (hw_rfkill) {
-                       set_bit(STATUS_RFKILL, &trans_pcie->status);
-                       if (test_and_clear_bit(STATUS_HCMD_ACTIVE,
-                                              &trans_pcie->status))
+                       set_bit(STATUS_RFKILL, &trans->status);
+                       if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
+                                              &trans->status))
                                IWL_DEBUG_RF_KILL(trans,
                                                  "Rfkill while SYNC HCMD in flight\n");
                        wake_up(&trans_pcie->wait_command_queue);
                } else {
-                       clear_bit(STATUS_RFKILL, &trans_pcie->status);
+                       clear_bit(STATUS_RFKILL, &trans->status);
                }
 
                handled |= CSR_INT_BIT_RF_KILL;
@@ -1005,7 +1002,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
        /* Re-enable all interrupts */
        /* only Re-enable if disabled by irq */
-       if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status))
+       if (test_bit(STATUS_INT_ENABLED, &trans->status))
                iwl_enable_interrupts(trans);
        /* Re-enable RF_KILL if it occurred */
        else if (handled & CSR_INT_BIT_RF_KILL)
@@ -1160,7 +1157,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
                 * the handler can be scheduled because of a previous
                 * interrupt.
                 */
-               if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
+               if (test_bit(STATUS_INT_ENABLED, &trans->status) &&
                    !trans_pcie->inta)
                        iwl_enable_interrupts(trans);
                return IRQ_NONE;
@@ -1290,7 +1287,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
        /* 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, &trans_pcie->status) &&
+       if (test_bit(STATUS_INT_ENABLED, &trans->status) &&
            !trans_pcie->inta)
                iwl_enable_interrupts(trans);
 
index cde9c16..eecd38e 100644 (file)
@@ -150,7 +150,6 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
  */
 static int iwl_pcie_apm_init(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret = 0;
        IWL_DEBUG_INFO(trans, "Init card's basic functions\n");
 
@@ -223,7 +222,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
        /* Clear the interrupt in APMG if the NIC is in RFKILL */
        iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL);
 
-       set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
+       set_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
 out:
        return ret;
@@ -249,10 +248,9 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
 
 static void iwl_pcie_apm_stop(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
 
-       clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
+       clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
        /* Stop device's DMA activity */
        iwl_pcie_apm_stop_master(trans);
@@ -582,7 +580,6 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
 static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                                   const struct fw_img *fw, bool run_in_rfkill)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
        bool hw_rfkill;
 
@@ -592,16 +589,14 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                return -EIO;
        }
 
-       clear_bit(STATUS_FW_ERROR, &trans_pcie->status);
-
        iwl_enable_rfkill_int(trans);
 
        /* If platform's RF_KILL switch is NOT set to KILL */
        hw_rfkill = iwl_is_rfkill_set(trans);
        if (hw_rfkill)
-               set_bit(STATUS_RFKILL, &trans_pcie->status);
+               set_bit(STATUS_RFKILL, &trans->status);
        else
-               clear_bit(STATUS_RFKILL, &trans_pcie->status);
+               clear_bit(STATUS_RFKILL, &trans->status);
        iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
        if (hw_rfkill && !run_in_rfkill)
                return -ERFKILL;
@@ -641,6 +636,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        unsigned long flags;
+       bool hw_rfkill;
 
        /* tell the device to stop sending interrupts */
        spin_lock_irqsave(&trans_pcie->irq_lock, flags);
@@ -657,7 +653,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
         * restart. So don't process again if the device is
         * already dead.
         */
-       if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) {
+       if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
                iwl_pcie_tx_stop(trans);
                iwl_pcie_rx_stop(trans);
 
@@ -681,17 +677,34 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        iwl_disable_interrupts(trans);
        spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
 
-       iwl_enable_rfkill_int(trans);
-
        /* stop and reset the on-board processor */
        iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
 
        /* clear all status bits */
-       clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
-       clear_bit(STATUS_INT_ENABLED, &trans_pcie->status);
-       clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
-       clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
-       clear_bit(STATUS_RFKILL, &trans_pcie->status);
+       clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+       clear_bit(STATUS_INT_ENABLED, &trans->status);
+       clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
+       clear_bit(STATUS_TPOWER_PMI, &trans->status);
+       clear_bit(STATUS_RFKILL, &trans->status);
+
+       /*
+        * Even if we stop the HW, we still want the RF kill
+        * interrupt
+        */
+       iwl_enable_rfkill_int(trans);
+
+       /*
+        * Check again since the RF kill state may have changed while
+        * all the interrupts were disabled, in this case we couldn't
+        * receive the RF kill interrupt and update the state in the
+        * op_mode.
+        */
+       hw_rfkill = iwl_is_rfkill_set(trans);
+       if (hw_rfkill)
+               set_bit(STATUS_RFKILL, &trans->status);
+       else
+               clear_bit(STATUS_RFKILL, &trans->status);
+       iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
 }
 
 static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
@@ -776,7 +789,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
 
 static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        bool hw_rfkill;
        int err;
 
@@ -798,21 +810,20 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
 
        hw_rfkill = iwl_is_rfkill_set(trans);
        if (hw_rfkill)
-               set_bit(STATUS_RFKILL, &trans_pcie->status);
+               set_bit(STATUS_RFKILL, &trans->status);
        else
-               clear_bit(STATUS_RFKILL, &trans_pcie->status);
+               clear_bit(STATUS_RFKILL, &trans->status);
        iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
 
        return 0;
 }
 
-static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans,
-                                  bool op_mode_leaving)
+static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       bool hw_rfkill;
        unsigned long flags;
 
+       /* disable interrupts - don't enable HW RF kill interrupt */
        spin_lock_irqsave(&trans_pcie->irq_lock, flags);
        iwl_disable_interrupts(trans);
        spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
@@ -824,27 +835,6 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans,
        spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
 
        iwl_pcie_disable_ict(trans);
-
-       if (!op_mode_leaving) {
-               /*
-                * Even if we stop the HW, we still want the RF kill
-                * interrupt
-                */
-               iwl_enable_rfkill_int(trans);
-
-               /*
-                * Check again since the RF kill state may have changed while
-                * all the interrupts were disabled, in this case we couldn't
-                * receive the RF kill interrupt and update the state in the
-                * op_mode.
-                */
-               hw_rfkill = iwl_is_rfkill_set(trans);
-               if (hw_rfkill)
-                       set_bit(STATUS_RFKILL, &trans_pcie->status);
-               else
-                       clear_bit(STATUS_RFKILL, &trans_pcie->status);
-               iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
-       }
 }
 
 static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
@@ -928,12 +918,10 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
 
 static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
        if (state)
-               set_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
+               set_bit(STATUS_TPOWER_PMI, &trans->status);
        else
-               clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
+               clear_bit(STATUS_TPOWER_PMI, &trans->status);
 }
 
 static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
@@ -1457,7 +1445,7 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
 
 static const struct iwl_trans_ops trans_ops_pcie = {
        .start_hw = iwl_trans_pcie_start_hw,
-       .stop_hw = iwl_trans_pcie_stop_hw,
+       .op_mode_leave = iwl_trans_pcie_op_mode_leave,
        .fw_alive = iwl_trans_pcie_fw_alive,
        .start_fw = iwl_trans_pcie_start_fw,
        .stop_device = iwl_trans_pcie_stop_device,
index a4ef5cc..8df2478 100644 (file)
@@ -207,7 +207,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
                IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
                        le32_to_cpu(txq->scratchbufs[i].scratch));
 
-       iwl_nic_error(trans);
+       iwl_trans_fw_error(trans);
 }
 
 /*
@@ -300,10 +300,8 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
                iwl_write32(trans, HBUS_TARG_WRPTR,
                            txq->q.write_ptr | (txq_id << 8));
        } else {
-               struct iwl_trans_pcie *trans_pcie =
-                       IWL_TRANS_GET_PCIE_TRANS(trans);
                /* if we're trying to save power */
-               if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) {
+               if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
                        /* wake up nic if it's powered down ...
                         * uCode will wake up, and interrupt us again, so next
                         * time we'll skip this part. */
@@ -1023,7 +1021,7 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
                if (nfreed++ > 0) {
                        IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
                                idx, q->write_ptr, q->read_ptr);
-                       iwl_nic_error(trans);
+                       iwl_trans_fw_error(trans);
                }
        }
 
@@ -1449,12 +1447,12 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
        iwl_pcie_cmdq_reclaim(trans, txq_id, index);
 
        if (!(meta->flags & CMD_ASYNC)) {
-               if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) {
+               if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) {
                        IWL_WARN(trans,
                                 "HCMD_ACTIVE already clear for command %s\n",
                                 get_cmd_string(trans_pcie, cmd->hdr.cmd));
                }
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
                               get_cmd_string(trans_pcie, cmd->hdr.cmd));
                wake_up(&trans_pcie->wait_command_queue);
@@ -1499,8 +1497,8 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
                       get_cmd_string(trans_pcie, cmd->id));
 
-       if (WARN(test_and_set_bit(STATUS_HCMD_ACTIVE,
-                                 &trans_pcie->status),
+       if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE,
+                                 &trans->status),
                 "Command %s: a command is already active!\n",
                 get_cmd_string(trans_pcie, cmd->id)))
                return -EIO;
@@ -1511,7 +1509,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
        if (cmd_idx < 0) {
                ret = cmd_idx;
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                IWL_ERR(trans,
                        "Error sending %s: enqueue_hcmd failed: %d\n",
                        get_cmd_string(trans_pcie, cmd->id), ret);
@@ -1523,8 +1521,8 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
 
                timeout -= COMMAND_POKE_TIMEOUT;
                ret = wait_event_timeout(trans_pcie->wait_command_queue,
-                                        !test_bit(STATUS_HCMD_ACTIVE,
-                                                  &trans_pcie->status),
+                                        !test_bit(STATUS_SYNC_HCMD_ACTIVE,
+                                                  &trans->status),
                                         COMMAND_POKE_TIMEOUT);
                if (ret)
                        break;
@@ -1552,17 +1550,17 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
                IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n",
                        q->read_ptr, q->write_ptr);
 
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
                               get_cmd_string(trans_pcie, cmd->id));
                ret = -ETIMEDOUT;
 
-               iwl_nic_error(trans);
+               iwl_trans_fw_error(trans);
 
                goto cancel;
        }
 
-       if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) {
+       if (test_bit(STATUS_FW_ERROR, &trans->status)) {
                IWL_ERR(trans, "FW error in SYNC CMD %s\n",
                        get_cmd_string(trans_pcie, cmd->id));
                dump_stack();
@@ -1571,7 +1569,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        }
 
        if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
-           test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+           test_bit(STATUS_RFKILL, &trans->status)) {
                IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n");
                ret = -ERFKILL;
                goto cancel;
@@ -1608,13 +1606,8 @@ cancel:
 
 int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
-       if (test_bit(STATUS_FW_ERROR, &trans_pcie->status))
-               return -EIO;
-
        if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
-           test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+           test_bit(STATUS_RFKILL, &trans->status)) {
                IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
                                  cmd->id);
                return -ERFKILL;