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)
1  2 
drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/sta.c

  #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)
        if (vif->type == NL80211_IFTYPE_STATION &&
            ap_sta_id != IWL_MVM_STATION_COUNT) {
                struct ieee80211_sta *sta;
 -              struct iwl_mvm_sta *mvm_sta;
  
                sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id],
                                                lockdep_is_held(&mvm->mutex));
 -              mvm_sta = (void *)sta->drv_priv;
 -              pos += scnprintf(buf+pos, bufsz-pos,
 -                               "ap_sta_id %d - reduced Tx power %d\n",
 -                               ap_sta_id, mvm_sta->bt_reduced_txpower);
 +              if (!IS_ERR_OR_NULL(sta)) {
 +                      struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 +
 +                      pos += scnprintf(buf+pos, bufsz-pos,
 +                                       "ap_sta_id %d - reduced Tx power %d\n",
 +                                       ap_sta_id,
 +                                       mvm_sta->bt_reduced_txpower);
 +              }
        }
  
        rcu_read_lock();
        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))      \
        } 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)
  {
                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
@@@ -261,6 -261,12 +261,12 @@@ int iwl_mvm_mac_setup_register(struct i
  
        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 +405,6 @@@ static void iwl_mvm_cleanup_iterator(vo
  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 +476,6 @@@ static void iwl_mvm_mac_stop(struct iee
        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 */
        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 +514,20 @@@ static struct iwl_mvm_phy_ctxt *iwl_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)
  {
        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
        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);
   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 +733,13 @@@ static void iwl_mvm_mac_remove_interfac
                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 +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,
                                     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,
                                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)) {
                                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)
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
  
+               iwl_mvm_recalc_multicast(mvm);
                /* reset rssi values */
                mvmvif->bf_data.ave_beacon_signal = 0;
  
                 */
                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 +1053,16 @@@ iwl_mvm_bss_info_changed_ap_ibss(struc
                                 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)
@@@ -1114,28 -1181,6 +1181,28 @@@ static void iwl_mvm_mac_sta_notify(stru
        }
  }
  
 +static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
 +                                     struct ieee80211_vif *vif,
 +                                     struct ieee80211_sta *sta)
 +{
 +      struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 +      struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 +
 +      /*
 +       * This is called before mac80211 does RCU synchronisation,
 +       * so here we already invalidate our internal RCU-protected
 +       * station pointer. The rest of the code will thus no longer
 +       * be able to find the station this way, and we don't rely
 +       * on further RCU synchronisation after the sta_state()
 +       * callback deleted the station.
 +       */
 +      mutex_lock(&mvm->mutex);
 +      if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id]))
 +              rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
 +                                 ERR_PTR(-ENOENT));
 +      mutex_unlock(&mvm->mutex);
 +}
 +
  static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@@ -1222,6 -1267,17 +1289,17 @@@ static int iwl_mvm_mac_set_rts_threshol
        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 -1400,12 +1422,12 @@@ static int iwl_mvm_mac_set_key(struct i
                 */
                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 -1611,7 +1633,7 @@@ static int iwl_mvm_add_chanctx(struct i
                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 -1655,7 +1677,7 @@@ static void iwl_mvm_change_chanctx(stru
                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 -1698,13 +1720,13 @@@ static int iwl_mvm_assign_vif_chanctx(s
                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.
         */
  
   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 -1754,9 +1776,9 @@@ static void iwl_mvm_unassign_vif_chanct
        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,15 -1851,16 +1873,17 @@@ 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,
        .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,
 +      .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove,
        .sta_state = iwl_mvm_mac_sta_state,
        .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,
@@@ -452,15 -452,8 +452,15 @@@ void iwl_mvm_sta_drained_wk(struct work
                        rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
                                                  lockdep_is_held(&mvm->mutex));
  
 -              /* This station is in use */
 -              if (!IS_ERR(sta))
 +              /*
 +               * This station is in use or RCU-removed; the latter happens in
 +               * managed mode, where mac80211 removes the station before we
 +               * can remove it from firmware (we can only do that after the
 +               * MAC is marked unassociated), and possibly while the deauth
 +               * frame to disconnect from the AP is still queued. Then, the
 +               * station pointer is -ENOENT when the last skb is reclaimed.
 +               */
 +              if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT)
                        continue;
  
                if (PTR_ERR(sta) == -EINVAL) {
@@@ -939,19 -932,6 +939,6 @@@ int iwl_mvm_sta_tx_agg_oper(struct iwl_
        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 -1110,8 +1117,8 @@@ static int iwl_mvm_send_sta_key(struct 
                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 -1275,8 +1282,8 @@@ int iwl_mvm_set_sta_key(struct iwl_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)