enum nl80211_tdls_operation oper,
                                 u16 reason_code, gfp_t gfp);
 
+/**
+ * ieee80211_reserve_tid - request to reserve a specific TID
+ *
+ * There is sometimes a need (such as in TDLS) for blocking the driver from
+ * using a specific TID so that the FW can use it for certain operations such
+ * as sending PTI requests. To make sure that the driver doesn't use that TID,
+ * this function must be called as it flushes out packets on this TID and marks
+ * it as blocked, so that any transmit for the station on this TID will be
+ * redirected to the alternative TID in the same AC.
+ *
+ * Note that this function blocks and may call back into the driver, so it
+ * should be called without driver locks held. Also note this function should
+ * only be called from the driver's @sta_state callback.
+ *
+ * @sta: the station to reserve the TID for
+ * @tid: the TID to reserve
+ *
+ * Returns: 0 on success, else on failure
+ */
+int ieee80211_reserve_tid(struct ieee80211_sta *sta, u8 tid);
+
+/**
+ * ieee80211_unreserve_tid - request to unreserve a specific TID
+ *
+ * Once there is no longer any need for reserving a certain TID, this function
+ * should be called, and no longer will packets have their TID modified for
+ * preventing use of this TID in the driver.
+ *
+ * Note that this function blocks and acquires a lock, so it should be called
+ * without driver locks held. Also note this function should only be called
+ * from the driver's @sta_state callback.
+ *
+ * @sta: the station
+ * @tid: the TID to unreserve
+ */
+void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
+
 /**
  * ieee80211_ie_split - split an IE buffer according to ordering
  *
 
        struct tid_ampdu_tx *tid_tx;
        int ret = 0;
 
+       if (WARN(sta->reserved_tid == tid,
+                "Requested to start BA session on reserved tid=%d", tid))
+               return -EINVAL;
+
        trace_api_start_tx_ba_session(pubsta, tid);
 
        if (WARN_ON_ONCE(!local->ops->ampdu_action))
                goto unlock;
        }
 
+       WARN(sta->reserved_tid == tid,
+            "Requested to stop BA session on reserved tid=%d", tid);
+
        if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
                /* already in progress stopping it */
                ret = 0;
 
        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
        IEEE80211_QUEUE_STOP_REASON_FLUSH,
        IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
+       IEEE80211_QUEUE_STOP_REASON_RESERVE_TID,
 
        IEEE80211_QUEUE_STOP_REASONS,
 };
 
 
        sta->sta_state = IEEE80211_STA_NONE;
 
+       /* Mark TID as unreserved */
+       sta->reserved_tid = IEEE80211_TID_UNRESERVED;
+
        ktime_get_ts(&uptime);
        sta->last_connected = uptime.tv_sec;
        ewma_init(&sta->avg_signal, 1024, 8);
 
        u32 bin_count;
 };
 
+/* Value to indicate no TID reservation */
+#define IEEE80211_TID_UNRESERVED       0xff
+
 /**
  * struct sta_info - STA information
  *
  *     AP only.
  * @cipher_scheme: optional cipher scheme for this station
  * @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
+ * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
  */
 struct sta_info {
        /* General information, mostly static */
        /* TDLS timeout data */
        unsigned long last_tdls_pkt_time;
 
+       u8 reserved_tid;
+
        /* keep last! */
        struct ieee80211_sta sta;
 };
 
 }
 EXPORT_SYMBOL(ieee80211_get_buffered_bc);
 
+int ieee80211_reserve_tid(struct ieee80211_sta *pubsta, u8 tid)
+{
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       int ret;
+       u32 queues;
+
+       lockdep_assert_held(&local->sta_mtx);
+
+       /* only some cases are supported right now */
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       if (WARN_ON(tid >= IEEE80211_NUM_UPS))
+               return -EINVAL;
+
+       if (sta->reserved_tid == tid) {
+               ret = 0;
+               goto out;
+       }
+
+       if (sta->reserved_tid != IEEE80211_TID_UNRESERVED) {
+               sdata_err(sdata, "TID reservation already active\n");
+               ret = -EALREADY;
+               goto out;
+       }
+
+       ieee80211_stop_vif_queues(sdata->local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
+
+       synchronize_net();
+
+       /* Tear down BA sessions so we stop aggregating on this TID */
+       if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+               set_sta_flag(sta, WLAN_STA_BLOCK_BA);
+               __ieee80211_stop_tx_ba_session(sta, tid,
+                                              AGG_STOP_LOCAL_REQUEST);
+       }
+
+       queues = BIT(sdata->vif.hw_queue[ieee802_1d_to_ac[tid]]);
+       __ieee80211_flush_queues(local, sdata, queues);
+
+       sta->reserved_tid = tid;
+
+       ieee80211_wake_vif_queues(local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
+
+       if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
+               clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
+
+       ret = 0;
+ out:
+       return ret;
+}
+EXPORT_SYMBOL(ieee80211_reserve_tid);
+
+void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
+{
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+       lockdep_assert_held(&sdata->local->sta_mtx);
+
+       /* only some cases are supported right now */
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               break;
+       default:
+               WARN_ON(1);
+               return;
+       }
+
+       if (tid != sta->reserved_tid) {
+               sdata_err(sdata, "TID to unreserve (%d) isn't reserved\n", tid);
+               return;
+       }
+
+       sta->reserved_tid = IEEE80211_TID_UNRESERVED;
+}
+EXPORT_SYMBOL(ieee80211_unreserve_tid);
+
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
                                 struct sk_buff *skb, int tid,
                                 enum ieee80211_band band)
 
        }
 }
 
+/**
+ * ieee80211_fix_reserved_tid - return the TID to use if this one is reserved
+ * @tid: the assumed-reserved TID
+ *
+ * Returns: the alternative TID to use, or 0 on error
+ */
+static inline u8 ieee80211_fix_reserved_tid(u8 tid)
+{
+       switch (tid) {
+       case 0:
+               return 3;
+       case 1:
+               return 2;
+       case 2:
+               return 1;
+       case 3:
+               return 0;
+       case 4:
+               return 5;
+       case 5:
+               return 4;
+       case 6:
+               return 7;
+       case 7:
+               return 6;
+       }
+
+       return 0;
+}
+
 static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
                                     struct sta_info *sta, struct sk_buff *skb)
 {
                }
        }
 
+       /* Check to see if this is a reserved TID */
+       if (sta && sta->reserved_tid == skb->priority)
+               skb->priority = ieee80211_fix_reserved_tid(skb->priority);
+
        /* look up which queue to use for frames with this 1d tag */
        return ieee802_1d_to_ac[skb->priority];
 }
                break;
 #endif
        case NL80211_IFTYPE_STATION:
+               /* might be a TDLS station */
+               sta = sta_info_get(sdata, skb->data);
+               if (sta)
+                       qos = sta->sta.wme;
+
                ra = sdata->u.mgd.bssid;
                break;
        case NL80211_IFTYPE_ADHOC: