mac80211: let flush() drop packets when possible
[cascardo/linux.git] / net / mac80211 / mlme.c
index 93af0f1..c071108 100644 (file)
@@ -157,14 +157,18 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct cfg80211_chan_def vht_chandef;
+       struct ieee80211_sta_ht_cap sta_ht_cap;
        u32 ht_cfreq, ret;
 
+       memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+       ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
        chandef->chan = channel;
        chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
        chandef->center_freq1 = channel->center_freq;
        chandef->center_freq2 = 0;
 
-       if (!ht_cap || !ht_oper || !sband->ht_cap.ht_supported) {
+       if (!ht_cap || !ht_oper || !sta_ht_cap.ht_supported) {
                ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
                goto out;
        }
@@ -174,6 +178,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        if (!(ht_cap->cap_info &
              cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) {
                ret = IEEE80211_STA_DISABLE_40MHZ;
+               vht_chandef = *chandef;
                goto out;
        }
 
@@ -197,7 +202,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        }
 
        /* check 40 MHz support, if we have it */
-       if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+       if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
                switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
                case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
                        chandef->width = NL80211_CHAN_WIDTH_40;
@@ -552,13 +557,17 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
        cap = vht_cap.cap;
 
        if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
-               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
-               cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+               if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
+                   bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+                       cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
        }
 
        if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
                cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
-               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
        }
 
        /*
@@ -775,11 +784,30 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                        WLAN_EID_QOS_CAPA,
                        WLAN_EID_RRM_ENABLED_CAPABILITIES,
                        WLAN_EID_MOBILITY_DOMAIN,
+                       WLAN_EID_FAST_BSS_TRANSITION,   /* reassoc only */
+                       WLAN_EID_RIC_DATA,              /* reassoc only */
                        WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
                };
-               noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
-                                            before_ht, ARRAY_SIZE(before_ht),
-                                            offset);
+               static const u8 after_ric[] = {
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+                       WLAN_EID_HT_CAPABILITY,
+                       WLAN_EID_BSS_COEX_2040,
+                       WLAN_EID_EXT_CAPABILITY,
+                       WLAN_EID_QOS_TRAFFIC_CAPA,
+                       WLAN_EID_TIM_BCAST_REQ,
+                       WLAN_EID_INTERWORKING,
+                       /* 60GHz doesn't happen right now */
+                       WLAN_EID_VHT_CAPABILITY,
+                       WLAN_EID_OPMODE_NOTIF,
+               };
+
+               noffset = ieee80211_ie_split_ric(assoc_data->ie,
+                                                assoc_data->ie_len,
+                                                before_ht,
+                                                ARRAY_SIZE(before_ht),
+                                                after_ric,
+                                                ARRAY_SIZE(after_ric),
+                                                offset);
                pos = skb_put(skb, noffset - offset);
                memcpy(pos, assoc_data->ie + offset, noffset - offset);
                offset = noffset;
@@ -813,6 +841,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                        WLAN_EID_TIM_BCAST_REQ,
                        WLAN_EID_INTERWORKING,
                };
+
+               /* RIC already taken above, so no need to handle here anymore */
                noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
                                             before_vht, ARRAY_SIZE(before_vht),
                                             offset);
@@ -1001,14 +1031,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->csa_chandef.chan;
 
-       sdata->vif.csa_active = false;
-
-       /* XXX: wait for a beacon first? */
-       if (sdata->csa_block_tx) {
-               ieee80211_wake_vif_queues(local, sdata,
-                                         IEEE80211_QUEUE_STOP_REASON_CSA);
-               sdata->csa_block_tx = false;
-       }
+       ifmgd->csa_waiting_bcn = true;
 
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
@@ -1019,6 +1042,37 @@ out:
        sdata_unlock(sdata);
 }
 
+static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       int ret;
+
+       sdata_assert_lock(sdata);
+
+       WARN_ON(!sdata->vif.csa_active);
+
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
+
+       sdata->vif.csa_active = false;
+       ifmgd->csa_waiting_bcn = false;
+
+       ret = drv_post_channel_switch(sdata);
+       if (ret) {
+               sdata_info(sdata,
+                          "driver post channel switch failed, disconnecting\n");
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               return;
+       }
+
+       cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
+}
+
 void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@ -1046,7 +1100,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
 
 static void
 ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
-                                u64 timestamp, struct ieee802_11_elems *elems,
+                                u64 timestamp, u32 device_timestamp,
+                                struct ieee802_11_elems *elems,
                                 bool beacon)
 {
        struct ieee80211_local *local = sdata->local;
@@ -1056,6 +1111,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
        struct ieee80211_csa_ie csa_ie;
+       struct ieee80211_channel_switch ch_switch;
        int res;
 
        sdata_assert_lock(sdata);
@@ -1110,21 +1166,31 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       if (local->use_chanctx) {
-               u32 num_chanctx = 0;
-               list_for_each_entry(chanctx, &local->chanctx_list, list)
-                      num_chanctx++;
+       if (local->use_chanctx &&
+           !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
+               sdata_info(sdata,
+                          "driver doesn't support chan-switch with channel contexts\n");
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
+               return;
+       }
 
-               if (num_chanctx > 1 ||
-                   !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
-                       sdata_info(sdata,
-                                  "not handling chan-switch with channel contexts\n");
-                       ieee80211_queue_work(&local->hw,
-                                            &ifmgd->csa_connection_drop_work);
-                       mutex_unlock(&local->chanctx_mtx);
-                       mutex_unlock(&local->mtx);
-                       return;
-               }
+       ch_switch.timestamp = timestamp;
+       ch_switch.device_timestamp = device_timestamp;
+       ch_switch.block_tx = csa_ie.mode;
+       ch_switch.chandef = csa_ie.chandef;
+       ch_switch.count = csa_ie.count;
+
+       if (drv_pre_channel_switch(sdata, &ch_switch)) {
+               sdata_info(sdata,
+                          "preparing for channel switch failed, disconnecting\n");
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
+               return;
        }
 
        res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1150,16 +1216,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&local->mtx);
 
+       cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
+                                         csa_ie.count);
+
        if (local->ops->channel_switch) {
                /* use driver's channel switch callback */
-               struct ieee80211_channel_switch ch_switch = {
-                       .timestamp = timestamp,
-                       .block_tx = csa_ie.mode,
-                       .chandef = csa_ie.chandef,
-                       .count = csa_ie.count,
-               };
-
-               drv_channel_switch(local, &ch_switch);
+               drv_channel_switch(local, sdata, &ch_switch);
                return;
        }
 
@@ -1226,8 +1288,11 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
                country_ie_len -= 3;
        }
 
-       if (have_chan_pwr)
+       if (have_chan_pwr && pwr_constr_elem)
                *pwr_reduction = *pwr_constr_elem;
+       else
+               *pwr_reduction = 0;
+
        return have_chan_pwr;
 }
 
@@ -1256,10 +1321,11 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
        int chan_pwr = 0, pwr_reduction_80211h = 0;
        int pwr_level_cisco, pwr_level_80211h;
        int new_ap_level;
+       __le16 capab = mgmt->u.probe_resp.capab_info;
 
-       if (country_ie && pwr_constr_ie &&
-           mgmt->u.probe_resp.capab_info &
-               cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) {
+       if (country_ie &&
+           (capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) ||
+            capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) {
                has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
                        sdata, channel, country_ie, country_ie_len,
                        pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
@@ -1538,7 +1604,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                } else {
                        ieee80211_send_nullfunc(local, sdata, 1);
                        /* Flush to get the tx status of nullfunc frame */
-                       ieee80211_flush_queues(local, sdata);
+                       ieee80211_flush_queues(local, sdata, false);
                }
        }
 
@@ -1580,6 +1646,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
        mutex_unlock(&sdata->local->mtx);
 }
 
+static bool
+__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       bool ret;
+       int ac;
+
+       if (local->hw.queues < IEEE80211_NUM_ACS)
+               return false;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+               int non_acm_ac;
+               unsigned long now = jiffies;
+
+               if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
+                   tx_tspec->admitted_time &&
+                   time_after(now, tx_tspec->time_slice_start + HZ)) {
+                       tx_tspec->consumed_tx_time = 0;
+                       tx_tspec->time_slice_start = now;
+
+                       if (tx_tspec->downgraded)
+                               tx_tspec->action =
+                                       TX_TSPEC_ACTION_STOP_DOWNGRADE;
+               }
+
+               switch (tx_tspec->action) {
+               case TX_TSPEC_ACTION_STOP_DOWNGRADE:
+                       /* take the original parameters */
+                       if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
+                               sdata_err(sdata,
+                                         "failed to set TX queue parameters for queue %d\n",
+                                         ac);
+                       tx_tspec->action = TX_TSPEC_ACTION_NONE;
+                       tx_tspec->downgraded = false;
+                       ret = true;
+                       break;
+               case TX_TSPEC_ACTION_DOWNGRADE:
+                       if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+                               tx_tspec->action = TX_TSPEC_ACTION_NONE;
+                               ret = true;
+                               break;
+                       }
+                       /* downgrade next lower non-ACM AC */
+                       for (non_acm_ac = ac + 1;
+                            non_acm_ac < IEEE80211_NUM_ACS;
+                            non_acm_ac++)
+                               if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
+                                       break;
+                       /* The loop will result in using BK even if it requires
+                        * admission control, such configuration makes no sense
+                        * and we have to transmit somehow - the AC selection
+                        * does the same thing.
+                        */
+                       if (drv_conf_tx(local, sdata, ac,
+                                       &sdata->tx_conf[non_acm_ac]))
+                               sdata_err(sdata,
+                                         "failed to set TX queue parameters for queue %d\n",
+                                         ac);
+                       tx_tspec->action = TX_TSPEC_ACTION_NONE;
+                       ret = true;
+                       schedule_delayed_work(&ifmgd->tx_tspec_wk,
+                               tx_tspec->time_slice_start + HZ - now + 1);
+                       break;
+               case TX_TSPEC_ACTION_NONE:
+                       /* nothing now */
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+       if (__ieee80211_sta_handle_tspec_ac_params(sdata))
+               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
+}
+
+static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       sdata = container_of(work, struct ieee80211_sub_if_data,
+                            u.mgd.tx_tspec_wk.work);
+       ieee80211_sta_handle_tspec_ac_params(sdata);
+}
+
 /* MLME */
 static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
@@ -1664,12 +1819,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                params.uapsd = uapsd;
 
                mlme_dbg(sdata,
-                        "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
+                        "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
                         queue, aci, acm,
                         params.aifs, params.cw_min, params.cw_max,
-                        params.txop, params.uapsd);
+                        params.txop, params.uapsd,
+                        ifmgd->tx_tspec[queue].downgraded);
                sdata->tx_conf[queue] = params;
-               if (drv_conf_tx(local, sdata, queue, &params))
+               if (!ifmgd->tx_tspec[queue].downgraded &&
+                   drv_conf_tx(local, sdata, queue, &params))
                        sdata_err(sdata,
                                  "failed to set TX queue parameters for queue %d\n",
                                  queue);
@@ -1854,18 +2011,23 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        /* disable per-vif ps */
        ieee80211_recalc_ps_vif(sdata);
 
-       /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
+       /*
+        * drop any frame before deauth/disassoc, this can be data or
+        * management frame. Since we are disconnecting, we should not
+        * insist sending these frames which can take time and delay
+        * the disconnection and possible the roaming.
+        */
        if (tx)
-               ieee80211_flush_queues(local, sdata);
+               ieee80211_flush_queues(local, sdata, true);
 
        /* deauthenticate/disassociate now */
        if (tx || frame_buf)
                ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype,
                                               reason, tx, frame_buf);
 
-       /* flush out frame */
+       /* flush out frame - make sure the deauth was actually sent */
        if (tx)
-               ieee80211_flush_queues(local, sdata);
+               ieee80211_flush_queues(local, sdata, false);
 
        /* clear bssid only after building the needed mgmt frames */
        memset(ifmgd->bssid, 0, ETH_ALEN);
@@ -1924,6 +2086,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_vif_release_channel(sdata);
 
        sdata->vif.csa_active = false;
+       ifmgd->csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1931,6 +2094,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&local->mtx);
 
+       /* existing TX TSPEC sessions no longer exist */
+       memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
+       cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
+
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 }
 
@@ -1983,9 +2150,46 @@ out:
        mutex_unlock(&local->mtx);
 }
 
+static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
+                                          struct ieee80211_hdr *hdr,
+                                          u16 tx_time)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+       int ac = ieee80211_ac_from_tid(tid);
+       struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+       unsigned long now = jiffies;
+
+       if (likely(!tx_tspec->admitted_time))
+               return;
+
+       if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+               tx_tspec->consumed_tx_time = 0;
+               tx_tspec->time_slice_start = now;
+
+               if (tx_tspec->downgraded) {
+                       tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
+                       schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+               }
+       }
+
+       if (tx_tspec->downgraded)
+               return;
+
+       tx_tspec->consumed_tx_time += tx_time;
+
+       if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
+               tx_tspec->downgraded = true;
+               tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
+               schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+       }
+}
+
 void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
-                            struct ieee80211_hdr *hdr, bool ack)
+                            struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
 {
+       ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
+
        if (!ieee80211_is_data(hdr->frame_control))
            return;
 
@@ -2040,7 +2244,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
                else
                        ssid_len = ssid[1];
 
-               ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
+               ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+                                        ssid + 2, ssid_len, NULL,
                                         0, (u32) -1, true, 0,
                                         ifmgd->associated->channel, false);
                rcu_read_unlock();
@@ -2048,8 +2253,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
 
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
        run_again(sdata, ifmgd->probe_timeout);
-       if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
-               ieee80211_flush_queues(sdata->local, sdata);
 }
 
 static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -2078,9 +2281,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
                                     "detected beacon loss from AP (missed %d beacons) - probing\n",
                                     beacon_loss_count);
 
-               ieee80211_cqm_rssi_notify(&sdata->vif,
-                                         NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
-                                         GFP_KERNEL);
+               ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL);
        }
 
        /*
@@ -2145,7 +2346,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
        else
                ssid_len = ssid[1];
 
-       skb = ieee80211_build_probe_req(sdata, cbss->bssid,
+       skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
                                        (u32) -1, cbss->channel,
                                        ssid + 2, ssid_len,
                                        NULL, 0, true);
@@ -2172,6 +2373,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                               true, frame_buf);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+       ifmgd->csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -2618,6 +2820,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        }
 
        ifmgd->aid = aid;
+       ifmgd->tdls_chan_switch_prohibited =
+               elems.ext_capab && elems.ext_capab_len >= 5 &&
+               (elems.ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
 
        /*
         * Some APs are erroneously not including some information in their
@@ -3196,6 +3401,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                }
        }
 
+       if (ifmgd->csa_waiting_bcn)
+               ieee80211_chswitch_post_beacon(sdata);
+
        if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
                return;
        ifmgd->beacon_crc = ncrc;
@@ -3204,6 +3412,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
+                                        rx_status->device_timestamp,
                                         &elems, true);
 
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
@@ -3335,8 +3544,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                break;
 
                        ieee80211_sta_process_chanswitch(sdata,
-                                                        rx_status->mactime,
-                                                        &elems, false);
+                                                rx_status->mactime,
+                                                rx_status->device_timestamp,
+                                                &elems, false);
                } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
                        ies_len = skb->len -
                                  offsetof(struct ieee80211_mgmt,
@@ -3357,8 +3567,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                &mgmt->u.action.u.ext_chan_switch.data;
 
                        ieee80211_sta_process_chanswitch(sdata,
-                                                        rx_status->mactime,
-                                                        &elems, false);
+                                                rx_status->mactime,
+                                                rx_status->device_timestamp,
+                                                &elems, false);
                }
                break;
        }
@@ -3456,7 +3667,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
                 * Direct probe is sent to broadcast address as some APs
                 * will not answer to direct packet in unassociated state.
                 */
-               ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
+               ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+                                        ssidie + 2, ssidie[1],
                                         NULL, 0, (u32) -1, true, 0,
                                         auth_data->bss->channel, false);
                rcu_read_unlock();
@@ -3665,11 +3877,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        if (local->quiescing)
                return;
 
-       if (sdata->vif.csa_active)
+       if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
 
        sdata->u.mgd.connection_loss = false;
@@ -3687,7 +3900,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
        if (local->quiescing)
                return;
 
-       if (sdata->vif.csa_active)
+       if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
 
        ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
@@ -3799,6 +4012,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
                    (unsigned long) sdata);
        setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
                    (unsigned long) sdata);
+       INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
+                         ieee80211_sta_handle_tspec_ac_params_wk);
 
        ifmgd->flags = 0;
        ifmgd->powersave = sdata->wdev.ps;
@@ -3810,6 +4025,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
                ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
        else
                ifmgd->req_smps = IEEE80211_SMPS_OFF;
+
+       /* Setup TDLS data */
+       spin_lock_init(&ifmgd->teardown_lock);
+       ifmgd->teardown_skb = NULL;
+       ifmgd->orig_teardown_skb = NULL;
 }
 
 /* scan finished notification */
@@ -3990,9 +4210,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_bss *bss = (void *)cbss->priv;
        struct sta_info *new_sta = NULL;
-       bool have_sta = false;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sta_ht_cap sta_ht_cap;
+       bool have_sta = false, is_override = false;
        int err;
 
+       sband = local->hw.wiphy->bands[cbss->channel->band];
+
        if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
                return -EINVAL;
 
@@ -4007,25 +4231,32 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                if (!new_sta)
                        return -ENOMEM;
        }
+
+       memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+       ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
+       is_override = (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) !=
+                     (sband->ht_cap.cap &
+                      IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+
+       if (new_sta || is_override) {
+               err = ieee80211_prep_channel(sdata, cbss);
+               if (err) {
+                       if (new_sta)
+                               sta_info_free(local, new_sta);
+                       return -EINVAL;
+               }
+       }
+
        if (new_sta) {
                u32 rates = 0, basic_rates = 0;
                bool have_higher_than_11mbit;
                int min_rate = INT_MAX, min_rate_index = -1;
                struct ieee80211_chanctx_conf *chanctx_conf;
-               struct ieee80211_supported_band *sband;
                const struct cfg80211_bss_ies *ies;
-               int shift;
+               int shift = ieee80211_vif_get_shift(&sdata->vif);
                u32 rate_flags;
 
-               sband = local->hw.wiphy->bands[cbss->channel->band];
-
-               err = ieee80211_prep_channel(sdata, cbss);
-               if (err) {
-                       sta_info_free(local, new_sta);
-                       return -EINVAL;
-               }
-               shift = ieee80211_vif_get_shift(&sdata->vif);
-
                rcu_read_lock();
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
                if (WARN_ON(!chanctx_conf)) {
@@ -4461,8 +4692,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
        rcu_read_unlock();
 
+       if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
+                (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
+            "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"))
+               sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+
        if (bss->wmm_used && bss->uapsd_supported &&
-           (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
+           (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) {
                assoc_data->uapsd = true;
                ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
        } else {
@@ -4672,6 +4908,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
        }
        if (ifmgd->auth_data)
                ieee80211_destroy_auth_data(sdata, false);
+       spin_lock_bh(&ifmgd->teardown_lock);
+       if (ifmgd->teardown_skb) {
+               kfree_skb(ifmgd->teardown_skb);
+               ifmgd->teardown_skb = NULL;
+               ifmgd->orig_teardown_skb = NULL;
+       }
+       spin_unlock_bh(&ifmgd->teardown_lock);
        del_timer_sync(&ifmgd->timer);
        sdata_unlock(sdata);
 }
@@ -4687,3 +4930,13 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
        cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
 }
 EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       trace_api_cqm_beacon_loss_notify(sdata->local, sdata);
+
+       cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify);