case IEEE80211_AMPDU_TX_START:
                ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
                if (!ret)
-                       ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+                       ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
                break;
        case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
 
                ath9k_ps_wakeup(sc);
                ret = ath_tx_aggr_start(sc, sta, tid, ssn);
                if (!ret)
-                       ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+                       ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
                ath9k_ps_restore(sc);
                break;
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
 
                rcu_assign_pointer(sta_info->agg[tid], tid_info);
                spin_unlock_bh(&ar->tx_ampdu_list_lock);
 
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
 
        case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
 
        enum ieee80211_ampdu_mlme_action action = params->action;
        u16 tid = params->tid;
        u16 *ssn = ¶ms->ssn;
+       int ret = 0;
 
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
                    action, tid);
                sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
                spin_unlock_bh(&sta_priv->ampdu_lock);
 
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
                break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
                spin_lock_bh(&sta_priv->ampdu_lock);
 
        mutex_unlock(&wcn->conf_mutex);
 
-       return 0;
+       return ret;
 }
 
 static const struct ieee80211_ops wcn36xx_ops = {
 
                                     "START: tid %d is not agg\'able\n", tid);
                        return -EINVAL;
                }
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
 
        case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
 
        if (tid_data->tfds_in_queue == 0) {
                D_HT("HW queue is empty\n");
                tid_data->agg.state = IL_AGG_ON;
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
        } else {
                D_HT("HW queue is NOT empty: %d packets in HW queue\n",
                     tid_data->tfds_in_queue);
 
                IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
                                    tid_data->agg.ssn);
                tid_data->agg.state = IWL_AGG_STARTING;
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
        } else {
                IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
                                    "next_reclaimed = %d\n",
 
 
        if (normalized_ssn == tid_data->next_reclaimed) {
                tid_data->state = IWL_AGG_STARTING;
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
        } else {
                tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+               ret = 0;
        }
 
-       ret = 0;
-
 out:
        spin_unlock_bh(&mvmsta->lock);
 
 
 
        switch (action) {
        case IEEE80211_AMPDU_TX_START:
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
        case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
 
                        rc = -EBUSY;
                        break;
                }
-               ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
+               rc = IEEE80211_AMPDU_TX_START_IMMEDIATE;
                break;
        case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
 
                break;
        case IEEE80211_AMPDU_TX_START:
                mtxq->agg_ssn = IEEE80211_SN_TO_SEQ(ssn);
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
        case IEEE80211_AMPDU_TX_STOP_CONT:
                mtxq->aggr = false;
                mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, -1);
 
                break;
        case IEEE80211_AMPDU_TX_START:
                mtxq->agg_ssn = IEEE80211_SN_TO_SEQ(ssn);
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
        case IEEE80211_AMPDU_TX_STOP_CONT:
                mtxq->aggr = false;
                mt7615_mcu_set_tx_ba(dev, params, 0);
 
                break;
        case IEEE80211_AMPDU_TX_START:
                mtxq->agg_ssn = IEEE80211_SN_TO_SEQ(ssn);
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
        case IEEE80211_AMPDU_TX_STOP_CONT:
                mtxq->aggr = false;
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 
                break;
        case IEEE80211_AMPDU_TX_START:
                msta->agg_ssn[tid] = ssn << 4;
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
        case IEEE80211_AMPDU_TX_STOP_CONT:
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
 
         * when the hw reorders frames due to aggregation.
         */
        if (sta_priv->wcid > WCID_END)
-               return 1;
+               return -ENOSPC;
 
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
                 */
                break;
        case IEEE80211_AMPDU_TX_START:
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
                break;
        case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
 
 
        tid_data->agg.agg_state = RTL_AGG_START;
 
-       ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-       return 0;
+       return IEEE80211_AMPDU_TX_START_IMMEDIATE;
 }
 
 int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 
 
        switch (params->action) {
        case IEEE80211_AMPDU_TX_START:
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               break;
+               return IEEE80211_AMPDU_TX_START_IMMEDIATE;
        case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
 
                else if ((vif->type == NL80211_IFTYPE_AP) ||
                         (vif->type == NL80211_IFTYPE_P2P_GO))
                        rsta->seq_start[tid] = seq_no;
-               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
-               status = 0;
+               status = IEEE80211_AMPDU_TX_START_IMMEDIATE;
                break;
 
        case IEEE80211_AMPDU_TX_STOP_CONT:
 
  *
  * @IEEE80211_AMPDU_RX_START: start RX aggregation
  * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation
- * @IEEE80211_AMPDU_TX_START: start TX aggregation
+ * @IEEE80211_AMPDU_TX_START: start TX aggregation, the driver must either
+ *     call ieee80211_start_tx_ba_cb_irqsafe() or return the special
+ *     status %IEEE80211_AMPDU_TX_START_IMMEDIATE.
  * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational
  * @IEEE80211_AMPDU_TX_STOP_CONT: stop TX aggregation but continue transmitting
  *     queued packets, now unaggregated. After all packets are transmitted the
        IEEE80211_AMPDU_TX_OPERATIONAL,
 };
 
+#define IEEE80211_AMPDU_TX_START_IMMEDIATE 1
+
 /**
  * struct ieee80211_ampdu_params - AMPDU action parameters
  *
         *
         * Even ``189`` would be wrong since 1 could be lost again.
         *
-        * Returns a negative error code on failure.
+        * Returns a negative error code on failure. The driver may return
+        * %IEEE80211_AMPDU_TX_START_IMMEDIATE for %IEEE80211_AMPDU_TX_START
+        * if the session can start immediately.
+        *
         * The callback can sleep.
         */
        int (*ampdu_action)(struct ieee80211_hw *hw,
 
 
        params.ssn = sta->tid_seq[tid] >> 4;
        ret = drv_ampdu_action(local, sdata, ¶ms);
-       if (ret) {
+       if (ret == IEEE80211_AMPDU_TX_START_IMMEDIATE) {
+               /*
+                * We didn't send the request yet, so don't need to check
+                * here if we already got a response, just mark as driver
+                * ready immediately.
+                */
+               set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state);
+       } else if (ret) {
                ht_dbg(sdata,
                       "BA request denied - HW unavailable for %pM tid %d\n",
                       sta->sta.addr, tid);