mac80211: split TX aggregation stop action
[deliverable/linux.git] / net / mac80211 / agg-tx.c
index dda8d7df4b54134c1c2c44064ea87405ecaf50d9..2f0ccbc5f13e82ee104ccd40b48cf9391162fbb3 100644 (file)
@@ -257,10 +257,25 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 {
        struct ieee80211_local *local = sta->local;
        struct tid_ampdu_tx *tid_tx;
+       enum ieee80211_ampdu_mlme_action action;
        int ret;
 
        lockdep_assert_held(&sta->ampdu_mlme.mtx);
 
+       switch (reason) {
+       case AGG_STOP_DECLINED:
+       case AGG_STOP_LOCAL_REQUEST:
+       case AGG_STOP_PEER_REQUEST:
+               action = IEEE80211_AMPDU_TX_STOP_CONT;
+               break;
+       case AGG_STOP_DESTROY_STA:
+               action = IEEE80211_AMPDU_TX_STOP_FLUSH;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
+
        spin_lock_bh(&sta->lock);
 
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
@@ -269,10 +284,19 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                return -ENOENT;
        }
 
-       /* if we're already stopping ignore any new requests to stop */
+       /*
+        * if we're already stopping ignore any new requests to stop
+        * unless we're destroying it in which case notify the driver
+        */
        if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
                spin_unlock_bh(&sta->lock);
-               return -EALREADY;
+               if (reason != AGG_STOP_DESTROY_STA)
+                       return -EALREADY;
+               ret = drv_ampdu_action(local, sta->sdata,
+                                      IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
+                                      &sta->sta, tid, NULL, 0);
+               WARN_ON_ONCE(ret);
+               goto remove_tid_tx;
        }
 
        if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
@@ -319,8 +343,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                                        WLAN_BACK_INITIATOR;
        tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
 
-       ret = drv_ampdu_action(local, sta->sdata,
-                              IEEE80211_AMPDU_TX_STOP,
+       ret = drv_ampdu_action(local, sta->sdata, action,
                               &sta->sta, tid, NULL, 0);
 
        /* HW shall not deny going back to legacy */
@@ -331,7 +354,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                 */
        }
 
-       return ret;
+       if (reason == AGG_STOP_DESTROY_STA) {
+ remove_tid_tx:
+               spin_lock_bh(&sta->lock);
+               ieee80211_remove_tid_tx(sta, tid);
+               spin_unlock_bh(&sta->lock);
+       }
+
+       return 0;
 }
 
 /*
This page took 0.026811 seconds and 5 git commands to generate.