Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
[linux-block.git] / drivers / net / wireless / iwlwifi / mvm / sta.c
index 274f44e2ef60e52d1fb39f66735c98563036449e..0fd96e4da4613457746e11909945e6716c21c47e 100644 (file)
@@ -22,7 +22,7 @@
  * USA
  *
  * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
+ * in the file called COPYING.
  *
  * Contact Information:
  *  Intel Linux Wireless <ilw@linux.intel.com>
@@ -101,8 +101,55 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        }
        add_sta_cmd.add_modify = update ? 1 : 0;
 
-       /* STA_FLG_FAT_EN_MSK ? */
-       /* STA_FLG_MIMO_EN_MSK ? */
+       add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_FAT_EN_MSK |
+                                                    STA_FLG_MIMO_EN_MSK);
+
+       switch (sta->bandwidth) {
+       case IEEE80211_STA_RX_BW_160:
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ);
+               /* fall through */
+       case IEEE80211_STA_RX_BW_80:
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ);
+               /* fall through */
+       case IEEE80211_STA_RX_BW_40:
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ);
+               /* fall through */
+       case IEEE80211_STA_RX_BW_20:
+               if (sta->ht_cap.ht_supported)
+                       add_sta_cmd.station_flags |=
+                               cpu_to_le32(STA_FLG_FAT_EN_20MHZ);
+               break;
+       }
+
+       switch (sta->rx_nss) {
+       case 1:
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
+               break;
+       case 2:
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2);
+               break;
+       case 3 ... 8:
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3);
+               break;
+       }
+
+       switch (sta->smps_mode) {
+       case IEEE80211_SMPS_AUTOMATIC:
+       case IEEE80211_SMPS_NUM_MODES:
+               WARN_ON(1);
+               break;
+       case IEEE80211_SMPS_STATIC:
+               /* override NSS */
+               add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK);
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO);
+               break;
+       case IEEE80211_SMPS_DYNAMIC:
+               add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT);
+               break;
+       case IEEE80211_SMPS_OFF:
+               /* nothing */
+               break;
+       }
 
        if (sta->ht_cap.ht_supported) {
                add_sta_cmd.station_flags_msk |=
@@ -340,6 +387,9 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
 
        if (vif->type == NL80211_IFTYPE_STATION &&
            mvmvif->ap_sta_id == mvm_sta->sta_id) {
+               /* flush its queues here since we are freeing mvm_sta */
+               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+
                /*
                 * Put a non-NULL since the fw station isn't removed.
                 * It will be removed after the MAC will be set as
@@ -348,9 +398,6 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
                                   ERR_PTR(-EINVAL));
 
-               /* flush its queues here since we are freeing mvm_sta */
-               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
-
                /* if we are associated - we can't remove the AP STA now */
                if (vif->bss_conf.assoc)
                        return ret;
@@ -686,7 +733,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        spin_lock_bh(&mvmsta->lock);
        tid_data = &mvmsta->tid_data[tid];
-       tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+       tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
        tid_data->txq_id = txq_id;
        *ssn = tid_data->ssn;
 
@@ -789,7 +836,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        switch (tid_data->state) {
        case IWL_AGG_ON:
-               tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+               tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);
 
                IWL_DEBUG_TX_QUEUES(mvm,
                                    "ssn = %d, next_recl = %d\n",
@@ -834,6 +881,34 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return err;
 }
 
+int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       u16 txq_id;
+
+       /*
+        * First set the agg state to OFF to avoid calling
+        * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty.
+        */
+       spin_lock_bh(&mvmsta->lock);
+       txq_id = tid_data->txq_id;
+       IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n",
+                           mvmsta->sta_id, tid, txq_id, tid_data->state);
+       tid_data->state = IWL_AGG_OFF;
+       spin_unlock_bh(&mvmsta->lock);
+
+       if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
+               IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+
+       iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
+       mvm->queue_to_mac80211[tid_data->txq_id] =
+                               IWL_INVALID_MAC80211_QUEUE;
+
+       return 0;
+}
+
 static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
 {
        int i;
@@ -870,7 +945,7 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
            mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT)
                return mvmvif->ap_sta_id;
 
-       return IWL_INVALID_STATION;
+       return IWL_MVM_STATION_COUNT;
 }
 
 static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
@@ -1018,7 +1093,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
 
        /* Get the station id from the mvm local station table */
        sta_id = iwl_mvm_get_key_sta_id(vif, sta);
-       if (sta_id == IWL_INVALID_STATION) {
+       if (sta_id == IWL_MVM_STATION_COUNT) {
                IWL_ERR(mvm, "Failed to find station id\n");
                return -EINVAL;
        }
@@ -1113,7 +1188,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
                return -ENOENT;
        }
 
-       if (sta_id == IWL_INVALID_STATION) {
+       if (sta_id == IWL_MVM_STATION_COUNT) {
                IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
                return 0;
        }
@@ -1179,7 +1254,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
        struct iwl_mvm_sta *mvm_sta;
        u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
 
-       if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION))
+       if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
                return;
 
        rcu_read_lock();