* @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted
  *     after TX status because the destination was asleep, it must not
  *     be modified again (no seqno assignment, crypto, etc.)
+ * @IEEE80211_TX_INTFL_MLME_CONN_TX: This frame was transmitted by the MLME
+ *     code for connection establishment, this indicates that its status
+ *     should kick the MLME state machine.
  * @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211
  *     MLME command (internal to mac80211 to figure out whether to send TX
  *     status to user space)
        IEEE80211_TX_CTL_NO_PS_BUFFER           = BIT(17),
        IEEE80211_TX_CTL_MORE_FRAMES            = BIT(18),
        IEEE80211_TX_INTFL_RETRANSMISSION       = BIT(19),
-       /* hole at 20, use later */
+       IEEE80211_TX_INTFL_MLME_CONN_TX         = BIT(20),
        IEEE80211_TX_INTFL_NL80211_FRAME_TX     = BIT(21),
        IEEE80211_TX_CTL_LDPC                   = BIT(22),
        IEEE80211_TX_CTL_STBC                   = BIT(23) | BIT(24),
 
                         "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
                         sdata->vif.addr, addr, sdata->u.ibss.bssid);
                ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0,
-                                   addr, sdata->u.ibss.bssid, NULL, 0, 0);
+                                   addr, sdata->u.ibss.bssid, NULL, 0, 0, 0);
        }
        return sta;
 }
         * has actually implemented this.
         */
        ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0,
-                           mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0);
+                           mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 
        bool beacon_crc_valid;
        u32 beacon_crc;
 
+       bool status_acked;
+       bool status_received;
+       __le16 status_fc;
+
        enum {
                IEEE80211_MFP_DISABLED,
                IEEE80211_MFP_OPTIONAL,
 void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata);
 void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
 void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
+                                 __le16 fc, bool acked);
 
 /* IBSS code */
 void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         u8 *extra, size_t extra_len, const u8 *bssid,
-                        const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
+                        const u8 *da, const u8 *key, u8 key_len, u8 key_idx,
+                        u32 tx_flags);
 void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                                    const u8 *bssid, u16 stype, u16 reason,
                                    bool send_frame, u8 *frame_buf);
 void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                              const u8 *ssid, size_t ssid_len,
                              const u8 *ie, size_t ie_len,
-                             u32 ratemask, bool directed, bool no_cck,
+                             u32 ratemask, bool directed, u32 tx_flags,
                              struct ieee80211_channel *channel, bool scan);
 
 void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
 
 #include "rate.h"
 #include "led.h"
 
-#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
-#define IEEE80211_AUTH_MAX_TRIES 3
-#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
-#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
-#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_AUTH_TIMEOUT         (HZ / 5)
+#define IEEE80211_AUTH_TIMEOUT_SHORT   (HZ / 10)
+#define IEEE80211_AUTH_MAX_TRIES       3
+#define IEEE80211_AUTH_WAIT_ASSOC      (HZ * 5)
+#define IEEE80211_ASSOC_TIMEOUT                (HZ / 5)
+#define IEEE80211_ASSOC_TIMEOUT_SHORT  (HZ / 10)
+#define IEEE80211_ASSOC_MAX_TRIES      3
 
 static int max_nullfunc_tries = 2;
 module_param(max_nullfunc_tries, int, 0644);
        drv_mgd_prepare_tx(local, sdata);
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
+                                               IEEE80211_TX_INTFL_MLME_CONN_TX;
        ieee80211_tx_skb(sdata, skb);
 }
 
                        ssid_len = ssid[1];
 
                ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
-                                        0, (u32) -1, true, false,
+                                        0, (u32) -1, true, 0,
                                         ifmgd->associated->channel, false);
                rcu_read_unlock();
        }
 static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_mgmt *mgmt, size_t len)
 {
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
        u8 *pos;
        struct ieee802_11_elems elems;
+       u32 tx_flags = 0;
 
        pos = mgmt->u.auth.variable;
        ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
                return;
        auth_data->expected_transaction = 4;
        drv_mgd_prepare_tx(sdata->local, sdata);
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+                          IEEE80211_TX_INTFL_MLME_CONN_TX;
        ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
                            elems.challenge - 2, elems.challenge_len + 2,
                            auth_data->bss->bssid, auth_data->bss->bssid,
                            auth_data->key, auth_data->key_len,
-                           auth_data->key_idx);
+                           auth_data->key_idx, tx_flags);
 }
 
 static enum rx_mgmt_action __must_check
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
+       u32 tx_flags = 0;
 
        lockdep_assert_held(&ifmgd->mtx);
 
        if (WARN_ON_ONCE(!auth_data))
                return -EINVAL;
 
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+                          IEEE80211_TX_INTFL_MLME_CONN_TX;
+
        auth_data->tries++;
 
        if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
                ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
                                    auth_data->data, auth_data->data_len,
                                    auth_data->bss->bssid,
-                                   auth_data->bss->bssid, NULL, 0, 0);
+                                   auth_data->bss->bssid, NULL, 0, 0,
+                                   tx_flags);
        } else {
                const u8 *ssidie;
 
                 * will not answer to direct packet in unassociated state.
                 */
                ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
-                                        NULL, 0, (u32) -1, true, false,
+                                        NULL, 0, (u32) -1, true, tx_flags,
                                         auth_data->bss->channel, false);
                rcu_read_unlock();
        }
 
-       auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-       run_again(ifmgd, auth_data->timeout);
+       if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+               auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+               run_again(ifmgd, auth_data->timeout);
+       }
 
        return 0;
 }
                   IEEE80211_ASSOC_MAX_TRIES);
        ieee80211_send_assoc(sdata);
 
-       assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
-       run_again(&sdata->u.mgd, assoc_data->timeout);
+       if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+               assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
+               run_again(&sdata->u.mgd, assoc_data->timeout);
+       }
 
        return 0;
 }
 
+void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
+                                 __le16 fc, bool acked)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       sdata->u.mgd.status_fc = fc;
+       sdata->u.mgd.status_acked = acked;
+       sdata->u.mgd.status_received = true;
+
+       ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
 void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
 
        mutex_lock(&ifmgd->mtx);
 
+       if (ifmgd->status_received) {
+               __le16 fc = ifmgd->status_fc;
+               bool status_acked = ifmgd->status_acked;
+
+               ifmgd->status_received = false;
+               if (ifmgd->auth_data &&
+                   (ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) {
+                       if (status_acked) {
+                               ifmgd->auth_data->timeout =
+                                       jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
+                               run_again(ifmgd, ifmgd->auth_data->timeout);
+                       } else {
+                               ifmgd->auth_data->timeout = jiffies - 1;
+                       }
+               } else if (ifmgd->assoc_data &&
+                          (ieee80211_is_assoc_req(fc) ||
+                           ieee80211_is_reassoc_req(fc))) {
+                       if (status_acked) {
+                               ifmgd->assoc_data->timeout =
+                                       jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
+                               run_again(ifmgd, ifmgd->assoc_data->timeout);
+                       } else {
+                               ifmgd->assoc_data->timeout = jiffies - 1;
+                       }
+               }
+       }
+
        if (ifmgd->auth_data &&
            time_after(jiffies, ifmgd->auth_data->timeout)) {
                if (ifmgd->auth_data->done) {
 
                        local->scan_req->ssids[i].ssid_len,
                        local->scan_req->ie, local->scan_req->ie_len,
                        local->scan_req->rates[band], false,
-                       local->scan_req->no_cck,
+                       local->scan_req->no_cck ?
+                               IEEE80211_TX_CTL_NO_CCK_RATE : 0,
                        local->hw.conf.channel, true);
 
        /*
 
        if (dropped)
                acked = false;
 
-       if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
+       if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+                          IEEE80211_TX_INTFL_MLME_CONN_TX)) {
                struct ieee80211_sub_if_data *sdata = NULL;
                struct ieee80211_sub_if_data *iter_sdata;
                u64 cookie = (unsigned long)skb;
                        sdata = rcu_dereference(local->p2p_sdata);
                }
 
-               if (!sdata)
+               if (!sdata) {
                        skb->dev = NULL;
-               else if (ieee80211_is_nullfunc(hdr->frame_control) ||
-                        ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+               } else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
+                       ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control,
+                                                    acked);
+               } else if (ieee80211_is_nullfunc(hdr->frame_control) ||
+                          ieee80211_is_qos_nullfunc(hdr->frame_control)) {
                        cfg80211_probe_status(sdata->dev, hdr->addr1,
                                              cookie, acked, GFP_ATOMIC);
                } else {
 
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         u8 *extra, size_t extra_len, const u8 *da,
-                        const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)
+                        const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
+                        u32 tx_flags)
 {
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
                WARN_ON(err);
        }
 
-       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
+                                       tx_flags;
        ieee80211_tx_skb(sdata, skb);
 }
 
 void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                              const u8 *ssid, size_t ssid_len,
                              const u8 *ie, size_t ie_len,
-                             u32 ratemask, bool directed, bool no_cck,
+                             u32 ratemask, bool directed, u32 tx_flags,
                              struct ieee80211_channel *channel, bool scan)
 {
        struct sk_buff *skb;
                                        ssid, ssid_len,
                                        ie, ie_len, directed);
        if (skb) {
-               if (no_cck)
-                       IEEE80211_SKB_CB(skb)->flags |=
-                               IEEE80211_TX_CTL_NO_CCK_RATE;
+               IEEE80211_SKB_CB(skb)->flags |= tx_flags;
                if (scan)
                        ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
                else