Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / iwlwifi / mvm / sta.c
index 274f44e2ef60e52d1fb39f66735c98563036449e..4d872d69577fe17b02a0670a7e0f36791ffd2bcc 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;