mac80211: let flush() drop packets when possible
[cascardo/linux.git] / net / mac80211 / mlme.c
index ba06cd0..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;
        }
 
        /*
@@ -1049,8 +1058,6 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
                sdata->csa_block_tx = false;
        }
 
-       cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
-
        sdata->vif.csa_active = false;
        ifmgd->csa_waiting_bcn = false;
 
@@ -1062,6 +1069,8 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
                                     &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)
@@ -1279,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;
 }
 
@@ -1309,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);
@@ -1591,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);
                }
        }
 
@@ -1998,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);
@@ -2263,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);
        }
 
        /*
@@ -4194,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;
 
@@ -4211,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)) {
@@ -4665,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 {
@@ -4898,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);