]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/mac80211/mlme.c
Merge tag 'for-3.10-rc1' of git://gitorious.org/linux-pwm/linux-pwm
[karo-tx-linux.git] / net / mac80211 / mlme.c
index 346ad4cfb01323cf471758dd5a6fcbdb12b338a6..29620bfc7a69663c34ab89b5bd03875eaaa4c25c 100644 (file)
@@ -56,7 +56,10 @@ MODULE_PARM_DESC(max_probe_tries,
  * probe on beacon miss before declaring the connection lost
  * default to what we want.
  */
-#define IEEE80211_BEACON_LOSS_COUNT    7
+static int beacon_loss_count = 7;
+module_param(beacon_loss_count, int, 0644);
+MODULE_PARM_DESC(beacon_loss_count,
+                "Number of beacon intervals before we decide beacon was lost.");
 
 /*
  * Time the connection can be idle before we probe
@@ -87,9 +90,6 @@ MODULE_PARM_DESC(probe_wait_ms,
  */
 #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
 
-#define TMR_RUNNING_TIMER      0
-#define TMR_RUNNING_CHANSW     1
-
 /*
  * All cfg80211 functions have to be called outside a locked
  * section so that they can acquire a lock themselves... This
@@ -289,6 +289,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        } else {
                /* 40 MHz (and 80 MHz) must be supported for VHT */
                ret = IEEE80211_STA_DISABLE_VHT;
+               /* also mark 40 MHz disabled */
+               ret |= IEEE80211_STA_DISABLE_40MHZ;
                goto out;
        }
 
@@ -303,12 +305,6 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                                               channel->band);
        vht_chandef.center_freq2 = 0;
 
-       if (vht_oper->center_freq_seg2_idx)
-               vht_chandef.center_freq2 =
-                       ieee80211_channel_to_frequency(
-                               vht_oper->center_freq_seg2_idx,
-                               channel->band);
-
        switch (vht_oper->chan_width) {
        case IEEE80211_VHT_CHANWIDTH_USE_HT:
                vht_chandef.width = chandef->width;
@@ -321,6 +317,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                break;
        case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
                vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+               vht_chandef.center_freq2 =
+                       ieee80211_channel_to_frequency(
+                               vht_oper->center_freq_seg2_idx,
+                               channel->band);
                break;
        default:
                if (verbose)
@@ -604,11 +604,11 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
        u8 *pos;
        u32 cap;
        struct ieee80211_sta_vht_cap vht_cap;
-       int i;
 
        BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
 
        memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+       ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
 
        /* determine capability flags */
        cap = vht_cap.cap;
@@ -631,37 +631,6 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
                        cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
                cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
 
-       if (!(ap_vht_cap->vht_cap_info &
-                       cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC)))
-               cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 |
-                        IEEE80211_VHT_CAP_RXSTBC_3 |
-                        IEEE80211_VHT_CAP_RXSTBC_4);
-
-       for (i = 0; i < 8; i++) {
-               int shift = i * 2;
-               u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift;
-               u16 ap_mcs, our_mcs;
-
-               ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) &
-                                                               mask) >> shift;
-               our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) &
-                                                               mask) >> shift;
-
-               if (our_mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
-                       continue;
-
-               switch (ap_mcs) {
-               default:
-                       if (our_mcs <= ap_mcs)
-                               break;
-                       /* fall through */
-               case IEEE80211_VHT_MCS_NOT_SUPPORTED:
-                       vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask);
-                       vht_cap.vht_mcs.rx_mcs_map |=
-                               cpu_to_le16(ap_mcs << shift);
-               }
-       }
-
        /* reserve and fill IE */
        pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
        ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@@ -987,6 +956,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
 {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        if (!ieee80211_sdata_running(sdata))
@@ -996,21 +966,22 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        if (!ifmgd->associated)
                goto out;
 
-       sdata->local->_oper_channel = sdata->local->csa_channel;
-       if (!sdata->local->ops->channel_switch) {
+       local->_oper_chandef = local->csa_chandef;
+
+       if (!local->ops->channel_switch) {
                /* call "hw_config" only if doing sw channel switch */
-               ieee80211_hw_config(sdata->local,
-                       IEEE80211_CONF_CHANGE_CHANNEL);
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
        } else {
                /* update the device channel directly */
-               sdata->local->hw.conf.channel = sdata->local->_oper_channel;
+               local->hw.conf.chandef = local->_oper_chandef;
        }
 
        /* XXX: shouldn't really modify cfg80211-owned data! */
-       ifmgd->associated->channel = sdata->local->_oper_channel;
+       ifmgd->associated->channel = local->_oper_chandef.chan;
 
        /* XXX: wait for a beacon first? */
-       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+       ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
  out:
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
@@ -1038,66 +1009,197 @@ static void ieee80211_chswitch_timer(unsigned long data)
 {
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-       if (sdata->local->quiescing) {
-               set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
-               return;
-       }
 
-       ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
 }
 
-void
+static void
 ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
-                                const struct ieee80211_channel_sw_ie *sw_elem,
-                                struct ieee80211_bss *bss, u64 timestamp)
+                                u64 timestamp, struct ieee802_11_elems *elems)
 {
-       struct cfg80211_bss *cbss =
-               container_of((void *)bss, struct cfg80211_bss, priv);
-       struct ieee80211_channel *new_ch;
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
-                                                     cbss->channel->band);
+       struct cfg80211_bss *cbss = ifmgd->associated;
+       struct ieee80211_bss *bss;
        struct ieee80211_chanctx *chanctx;
+       enum ieee80211_band new_band;
+       int new_freq;
+       u8 new_chan_no;
+       u8 count;
+       u8 mode;
+       struct ieee80211_channel *new_chan;
+       struct cfg80211_chan_def new_chandef = {};
+       struct cfg80211_chan_def new_vht_chandef = {};
+       const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+       const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
+       int secondary_channel_offset = -1;
 
        ASSERT_MGD_MTX(ifmgd);
 
-       if (!ifmgd->associated)
+       if (!cbss)
                return;
 
-       if (sdata->local->scanning)
+       if (local->scanning)
                return;
 
-       /* Disregard subsequent beacons if we are already running a timer
-          processing a CSA */
-
+       /* disregard subsequent announcements if we are already processing */
        if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
                return;
 
-       new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
-       if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) {
+       sec_chan_offs = elems->sec_chan_offs;
+       wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
+
+       if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
+                           IEEE80211_STA_DISABLE_40MHZ)) {
+               sec_chan_offs = NULL;
+               wide_bw_chansw_ie = NULL;
+       }
+
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
+               wide_bw_chansw_ie = NULL;
+
+       if (elems->ext_chansw_ie) {
+               if (!ieee80211_operating_class_to_band(
+                               elems->ext_chansw_ie->new_operating_class,
+                               &new_band)) {
+                       sdata_info(sdata,
+                                  "cannot understand ECSA IE operating class %d, disconnecting\n",
+                                  elems->ext_chansw_ie->new_operating_class);
+                       ieee80211_queue_work(&local->hw,
+                                            &ifmgd->csa_connection_drop_work);
+               }
+               new_chan_no = elems->ext_chansw_ie->new_ch_num;
+               count = elems->ext_chansw_ie->count;
+               mode = elems->ext_chansw_ie->mode;
+       } else if (elems->ch_switch_ie) {
+               new_band = cbss->channel->band;
+               new_chan_no = elems->ch_switch_ie->new_ch_num;
+               count = elems->ch_switch_ie->count;
+               mode = elems->ch_switch_ie->mode;
+       } else {
+               /* nothing here we understand */
+               return;
+       }
+
+       bss = (void *)cbss->priv;
+
+       new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
+       new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+       if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
                sdata_info(sdata,
                           "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
                           ifmgd->associated->bssid, new_freq);
-               ieee80211_queue_work(&sdata->local->hw,
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               return;
+       }
+
+       if (sec_chan_offs) {
+               secondary_channel_offset = sec_chan_offs->sec_chan_offs;
+       } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
+               /* if HT is enabled and the IE not present, it's still HT */
+               secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+       }
+
+       switch (secondary_channel_offset) {
+       default:
+               /* secondary_channel_offset was present but is invalid */
+       case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+               cfg80211_chandef_create(&new_chandef, new_chan,
+                                       NL80211_CHAN_HT20);
+               break;
+       case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+               cfg80211_chandef_create(&new_chandef, new_chan,
+                                       NL80211_CHAN_HT40PLUS);
+               break;
+       case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+               cfg80211_chandef_create(&new_chandef, new_chan,
+                                       NL80211_CHAN_HT40MINUS);
+               break;
+       case -1:
+               cfg80211_chandef_create(&new_chandef, new_chan,
+                                       NL80211_CHAN_NO_HT);
+               break;
+       }
+
+       if (wide_bw_chansw_ie) {
+               new_vht_chandef.chan = new_chan;
+               new_vht_chandef.center_freq1 =
+                       ieee80211_channel_to_frequency(
+                               wide_bw_chansw_ie->new_center_freq_seg0,
+                               new_band);
+
+               switch (wide_bw_chansw_ie->new_channel_width) {
+               default:
+                       /* hmmm, ignore VHT and use HT if present */
+               case IEEE80211_VHT_CHANWIDTH_USE_HT:
+                       new_vht_chandef.chan = NULL;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_80MHZ:
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_160MHZ:
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+                       /* field is otherwise reserved */
+                       new_vht_chandef.center_freq2 =
+                               ieee80211_channel_to_frequency(
+                                       wide_bw_chansw_ie->new_center_freq_seg1,
+                                       new_band);
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+                       break;
+               }
+               if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
+                   new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
+                       chandef_downgrade(&new_vht_chandef);
+               if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
+                   new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
+                       chandef_downgrade(&new_vht_chandef);
+               if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
+                   new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
+                       chandef_downgrade(&new_vht_chandef);
+       }
+
+       /* if VHT data is there validate & use it */
+       if (new_vht_chandef.chan) {
+               if (!cfg80211_chandef_compatible(&new_vht_chandef,
+                                                &new_chandef)) {
+                       sdata_info(sdata,
+                                  "AP %pM CSA has inconsistent channel data, disconnecting\n",
+                                  ifmgd->associated->bssid);
+                       ieee80211_queue_work(&local->hw,
+                                            &ifmgd->csa_connection_drop_work);
+                       return;
+               }
+               new_chandef = new_vht_chandef;
+       }
+
+       if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
+                                    IEEE80211_CHAN_DISABLED)) {
+               sdata_info(sdata,
+                          "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
+                          ifmgd->associated->bssid, new_freq,
+                          new_chandef.width, new_chandef.center_freq1,
+                          new_chandef.center_freq2);
+               ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                return;
        }
 
        ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
 
-       if (sdata->local->use_chanctx) {
+       if (local->use_chanctx) {
                sdata_info(sdata,
                           "not handling channel switch with channel contexts\n");
-               ieee80211_queue_work(&sdata->local->hw,
+               ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                return;
        }
 
-       mutex_lock(&sdata->local->chanctx_mtx);
+       mutex_lock(&local->chanctx_mtx);
        if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
-               mutex_unlock(&sdata->local->chanctx_mtx);
+               mutex_unlock(&local->chanctx_mtx);
                return;
        }
        chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
@@ -1105,39 +1207,39 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (chanctx->refcount > 1) {
                sdata_info(sdata,
                           "channel switch with multiple interfaces on the same channel, disconnecting\n");
-               ieee80211_queue_work(&sdata->local->hw,
+               ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
-               mutex_unlock(&sdata->local->chanctx_mtx);
+               mutex_unlock(&local->chanctx_mtx);
                return;
        }
-       mutex_unlock(&sdata->local->chanctx_mtx);
+       mutex_unlock(&local->chanctx_mtx);
 
-       sdata->local->csa_channel = new_ch;
+       local->csa_chandef = new_chandef;
 
-       if (sw_elem->mode)
-               ieee80211_stop_queues_by_reason(&sdata->local->hw,
+       if (mode)
+               ieee80211_stop_queues_by_reason(&local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       if (sdata->local->ops->channel_switch) {
+       if (local->ops->channel_switch) {
                /* use driver's channel switch callback */
                struct ieee80211_channel_switch ch_switch = {
                        .timestamp = timestamp,
-                       .block_tx = sw_elem->mode,
-                       .channel = new_ch,
-                       .count = sw_elem->count,
+                       .block_tx = mode,
+                       .chandef = new_chandef,
+                       .count = count,
                };
 
-               drv_channel_switch(sdata->local, &ch_switch);
+               drv_channel_switch(local, &ch_switch);
                return;
        }
 
        /* channel switch handled in software */
-       if (sw_elem->count <= 1)
-               ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
+       if (count <= 1)
+               ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(sw_elem->count *
-                                        cbss->beacon_interval));
+                         TU_TO_EXP_TIME(count * cbss->beacon_interval));
 }
 
 static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@ -1383,6 +1485,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
        }
 
        ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_PS);
 }
 
@@ -1435,16 +1538,14 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
 
        if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
            !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
-               netif_tx_stop_all_queues(sdata->dev);
-
-               if (drv_tx_frames_pending(local))
+               if (drv_tx_frames_pending(local)) {
                        mod_timer(&local->dynamic_ps_timer, jiffies +
                                  msecs_to_jiffies(
                                  local->hw.conf.dynamic_ps_timeout));
-               else {
+               else {
                        ieee80211_send_nullfunc(local, sdata, 1);
                        /* Flush to get the tx status of nullfunc frame */
-                       drv_flush(local, false);
+                       ieee80211_flush_queues(local, sdata);
                }
        }
 
@@ -1455,9 +1556,6 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                local->hw.conf.flags |= IEEE80211_CONF_PS;
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
        }
-
-       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-               netif_tx_wake_all_queues(sdata->dev);
 }
 
 void ieee80211_dynamic_ps_timer(unsigned long data)
@@ -1563,6 +1661,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
                params.cw_min = ecw2cw(pos[1] & 0x0f);
                params.txop = get_unaligned_le16(pos + 2);
+               params.acm = acm;
                params.uapsd = uapsd;
 
                mlme_dbg(sdata,
@@ -1650,7 +1749,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                bss_conf->assoc_capability, bss->has_erp_value, bss->erp_value);
 
        sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec(
-               IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int));
+               beacon_loss_count * bss_conf->beacon_int));
 
        sdata->u.mgd.associated = cbss;
        memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
@@ -1663,18 +1762,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                rcu_read_lock();
                ies = rcu_dereference(cbss->ies);
                if (ies) {
-                       u8 noa[2];
                        int ret;
 
                        ret = cfg80211_get_p2p_attr(
                                        ies->data, ies->len,
                                        IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
-                                       noa, sizeof(noa));
+                                       (u8 *) &bss_conf->p2p_noa_attr,
+                                       sizeof(bss_conf->p2p_noa_attr));
                        if (ret >= 2) {
-                               bss_conf->p2p_oppps = noa[1] & 0x80;
-                               bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+                               sdata->u.mgd.p2p_noa_index =
+                                       bss_conf->p2p_noa_attr.index;
                                bss_info_changed |= BSS_CHANGED_P2P_PS;
-                               sdata->u.mgd.p2p_noa_index = noa[0];
                        }
                }
                rcu_read_unlock();
@@ -1718,7 +1816,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        ieee80211_recalc_smps(sdata);
        ieee80211_recalc_ps_vif(sdata);
 
-       netif_tx_start_all_queues(sdata->dev);
        netif_carrier_on(sdata->dev);
 }
 
@@ -1741,22 +1838,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_stop_poll(sdata);
 
        ifmgd->associated = NULL;
-
-       /*
-        * we need to commit the associated = NULL change because the
-        * scan code uses that to determine whether this iface should
-        * go to/wake up from powersave or not -- and could otherwise
-        * wake the queues erroneously.
-        */
-       smp_mb();
-
-       /*
-        * Thus, we can only afterwards stop the queues -- to account
-        * for the case where another CPU is finishing a scan at this
-        * time -- we don't want the scan code to enable queues.
-        */
-
-       netif_tx_stop_all_queues(sdata->dev);
        netif_carrier_off(sdata->dev);
 
        /*
@@ -1775,7 +1856,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
        if (tx)
-               drv_flush(local, false);
+               ieee80211_flush_queues(local, sdata);
 
        /* deauthenticate/disassociate now */
        if (tx || frame_buf)
@@ -1784,7 +1865,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        /* flush out frame */
        if (tx)
-               drv_flush(local, false);
+               ieee80211_flush_queues(local, sdata);
 
        /* clear bssid only after building the needed mgmt frames */
        memset(ifmgd->bssid, 0, ETH_ALEN);
@@ -1799,12 +1880,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        changed |= BSS_CHANGED_ASSOC;
        sdata->vif.bss_conf.assoc = false;
 
-       sdata->vif.bss_conf.p2p_ctwindow = 0;
-       sdata->vif.bss_conf.p2p_oppps = false;
+       ifmgd->p2p_noa_index = -1;
+       memset(&sdata->vif.bss_conf.p2p_noa_attr, 0,
+              sizeof(sdata->vif.bss_conf.p2p_noa_attr));
 
-       /* on the next assoc, re-program HT parameters */
+       /* on the next assoc, re-program HT/VHT parameters */
        memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
        memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
+       memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
+       memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
 
        sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
 
@@ -1830,8 +1914,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        del_timer_sync(&sdata->u.mgd.timer);
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
 
-       sdata->u.mgd.timers_running = 0;
-
        sdata->vif.bss_conf.dtim_period = 0;
 
        ifmgd->flags = 0;
@@ -1956,7 +2038,7 @@ 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(ifmgd, ifmgd->probe_timeout);
        if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
-               drv_flush(sdata->local, false);
+               ieee80211_flush_queues(sdata->local, sdata);
 }
 
 static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -1980,12 +2062,15 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       if (beacon)
+       if (beacon) {
                mlme_dbg_ratelimited(sdata,
-                                    "detected beacon loss from AP - probing\n");
+                                    "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_rssi_notify(&sdata->vif,
+                                         NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+                                         GFP_KERNEL);
+       }
 
        /*
         * The driver/our work has already reported this event or the
@@ -2079,6 +2164,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                               true, frame_buf);
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&ifmgd->mtx);
 
@@ -2130,7 +2216,6 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)
 
        trace_api_beacon_loss(sdata);
 
-       WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
        sdata->u.mgd.connection_loss = false;
        ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
 }
@@ -2180,7 +2265,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
        u32 tx_flags = 0;
 
        pos = mgmt->u.auth.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
        if (!elems.challenge)
                return;
        auth_data->expected_transaction = 4;
@@ -2445,7 +2530,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        }
 
        pos = mgmt->u.assoc_resp.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
 
        if (!elems.supp_rates) {
                sdata_info(sdata, "no SuppRates element in AssocResp\n");
@@ -2614,13 +2699,13 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                   capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
 
        pos = mgmt->u.assoc_resp.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
 
        if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-           elems.timeout_int && elems.timeout_int_len == 5 &&
-           elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+           elems.timeout_int &&
+           elems.timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
                u32 tu, ms;
-               tu = get_unaligned_le32(elems.timeout_int + 1);
+               tu = le32_to_cpu(elems.timeout_int->value);
                ms = tu * 1024 / 1000;
                sdata_info(sdata,
                           "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
@@ -2669,6 +2754,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_channel *channel;
        bool need_ps = false;
 
+       lockdep_assert_held(&sdata->u.mgd.mtx);
+
        if ((sdata->u.mgd.associated &&
             ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
            (sdata->u.mgd.assoc_data &&
@@ -2683,7 +2770,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       if (elems->ds_params && elems->ds_params_len == 1)
+       if (elems->ds_params)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0],
                                                      rx_status->band);
        else
@@ -2699,7 +2786,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        if (bss)
                ieee80211_rx_bss_put(local, bss);
 
-       if (!sdata->u.mgd.associated)
+       if (!sdata->u.mgd.associated ||
+           !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))
                return;
 
        if (need_ps) {
@@ -2708,10 +2796,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                mutex_unlock(&local->iflist_mtx);
        }
 
-       if (elems->ch_switch_ie &&
-           memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0)
-               ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie,
-                                                bss, rx_status->mactime);
+       ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems);
+
 }
 
 
@@ -2736,7 +2822,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
                return;
 
        ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
-                               &elems);
+                              false, &elems);
 
        ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 
@@ -2819,7 +2905,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
            ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
                ieee802_11_parse_elems(mgmt->u.beacon.variable,
-                                      len - baselen, &elems);
+                                      len - baselen, false, &elems);
 
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
                ifmgd->assoc_data->have_beacon = true;
@@ -2929,7 +3015,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
        ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
-                                         len - baselen, &elems,
+                                         len - baselen, false, &elems,
                                          care_about_ies, ncrc);
 
        if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
@@ -2961,22 +3047,30 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        }
 
        if (sdata->vif.p2p) {
-               u8 noa[2];
+               struct ieee80211_p2p_noa_attr noa = {};
                int ret;
 
                ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
                                            len - baselen,
                                            IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
-                                           noa, sizeof(noa));
-               if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) {
-                       bss_conf->p2p_oppps = noa[1] & 0x80;
-                       bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+                                           (u8 *) &noa, sizeof(noa));
+               if (ret >= 2) {
+                       if (sdata->u.mgd.p2p_noa_index != noa.index) {
+                               /* valid noa_attr and index changed */
+                               sdata->u.mgd.p2p_noa_index = noa.index;
+                               memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa));
+                               changed |= BSS_CHANGED_P2P_PS;
+                               /*
+                                * make sure we update all information, the CRC
+                                * mechanism doesn't look at P2P attributes.
+                                */
+                               ifmgd->beacon_crc_valid = false;
+                       }
+               } else if (sdata->u.mgd.p2p_noa_index != -1) {
+                       /* noa_attr not found and we had valid noa_attr before */
+                       sdata->u.mgd.p2p_noa_index = -1;
+                       memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr));
                        changed |= BSS_CHANGED_P2P_PS;
-                       sdata->u.mgd.p2p_noa_index = noa[0];
-                       /*
-                        * make sure we update all information, the CRC
-                        * mechanism doesn't look at P2P attributes.
-                        */
                        ifmgd->beacon_crc_valid = false;
                }
        }
@@ -3018,7 +3112,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                changed |= BSS_CHANGED_DTIM_PERIOD;
        }
 
-       if (elems.erp_info && elems.erp_info_len >= 1) {
+       if (elems.erp_info) {
                erp_valid = true;
                erp_value = elems.erp_info[0];
        } else {
@@ -3068,6 +3162,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        enum rx_mgmt_action rma = RX_MGMT_NONE;
        u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
        u16 fc;
+       struct ieee802_11_elems elems;
+       int ies_len;
 
        rx_status = (struct ieee80211_rx_status *) skb->cb;
        mgmt = (struct ieee80211_mgmt *) skb->data;
@@ -3097,14 +3193,48 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss);
                break;
        case IEEE80211_STYPE_ACTION:
-               switch (mgmt->u.action.category) {
-               case WLAN_CATEGORY_SPECTRUM_MGMT:
+               if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
+                       ies_len = skb->len -
+                                 offsetof(struct ieee80211_mgmt,
+                                          u.action.u.chan_switch.variable);
+
+                       if (ies_len < 0)
+                               break;
+
+                       ieee802_11_parse_elems(
+                               mgmt->u.action.u.chan_switch.variable,
+                               ies_len, true, &elems);
+
+                       if (elems.parse_error)
+                               break;
+
                        ieee80211_sta_process_chanswitch(sdata,
-                                       &mgmt->u.action.u.chan_switch.sw_elem,
-                                       (void *)ifmgd->associated->priv,
-                                       rx_status->mactime);
-                       break;
+                                                        rx_status->mactime,
+                                                        &elems);
+               } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
+                       ies_len = skb->len -
+                                 offsetof(struct ieee80211_mgmt,
+                                          u.action.u.ext_chan_switch.variable);
+
+                       if (ies_len < 0)
+                               break;
+
+                       ieee802_11_parse_elems(
+                               mgmt->u.action.u.ext_chan_switch.variable,
+                               ies_len, true, &elems);
+
+                       if (elems.parse_error)
+                               break;
+
+                       /* for the handling code pretend this was also an IE */
+                       elems.ext_chansw_ie =
+                               &mgmt->u.action.u.ext_chan_switch.data;
+
+                       ieee80211_sta_process_chanswitch(sdata,
+                                                        rx_status->mactime,
+                                                        &elems);
                }
+               break;
        }
        mutex_unlock(&ifmgd->mtx);
 
@@ -3140,15 +3270,8 @@ static void ieee80211_sta_timer(unsigned long data)
 {
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
 
-       if (local->quiescing) {
-               set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
-               return;
-       }
-
-       ieee80211_queue_work(&local->hw, &sdata->work);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
 static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
@@ -3500,72 +3623,6 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
        }
 }
 
-#ifdef CONFIG_PM
-void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-       /*
-        * Stop timers before deleting work items, as timers
-        * could race and re-add the work-items. They will be
-        * re-established on connection.
-        */
-       del_timer_sync(&ifmgd->conn_mon_timer);
-       del_timer_sync(&ifmgd->bcn_mon_timer);
-
-       /*
-        * we need to use atomic bitops for the running bits
-        * only because both timers might fire at the same
-        * time -- the code here is properly synchronised.
-        */
-
-       cancel_work_sync(&ifmgd->request_smps_work);
-
-       cancel_work_sync(&ifmgd->monitor_work);
-       cancel_work_sync(&ifmgd->beacon_connection_loss_work);
-       cancel_work_sync(&ifmgd->csa_connection_drop_work);
-       if (del_timer_sync(&ifmgd->timer))
-               set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
-
-       if (del_timer_sync(&ifmgd->chswitch_timer))
-               set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
-       cancel_work_sync(&ifmgd->chswitch_work);
-}
-
-void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-       mutex_lock(&ifmgd->mtx);
-       if (!ifmgd->associated) {
-               mutex_unlock(&ifmgd->mtx);
-               return;
-       }
-
-       if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
-               sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
-               mlme_dbg(sdata, "driver requested disconnect after resume\n");
-               ieee80211_sta_connection_lost(sdata,
-                                             ifmgd->associated->bssid,
-                                             WLAN_REASON_UNSPECIFIED,
-                                             true);
-               mutex_unlock(&ifmgd->mtx);
-               return;
-       }
-       mutex_unlock(&ifmgd->mtx);
-
-       if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
-               add_timer(&ifmgd->timer);
-       if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
-               add_timer(&ifmgd->chswitch_timer);
-       ieee80211_sta_reset_beacon_monitor(sdata);
-
-       mutex_lock(&sdata->local->mtx);
-       ieee80211_restart_sta_timer(sdata);
-       mutex_unlock(&sdata->local->mtx);
-}
-#endif
-
 /* interface setup */
 void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
 {
@@ -3590,8 +3647,9 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
 
        ifmgd->flags = 0;
        ifmgd->powersave = sdata->wdev.ps;
-       ifmgd->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
-       ifmgd->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
+       ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues;
+       ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
+       ifmgd->p2p_noa_index = -1;
 
        mutex_init(&ifmgd->mtx);
 
@@ -4089,6 +4147,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
        }
 
+       if (req->flags & ASSOC_REQ_DISABLE_VHT)
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
        /* Also disable HT if we don't support it or the AP doesn't use WMM */
        sband = local->hw.wiphy->bands[req->bss->channel->band];
        if (!sband->ht_cap.ht_supported ||
@@ -4112,6 +4173,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
               sizeof(ifmgd->ht_capa_mask));
 
+       memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa));
+       memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask,
+              sizeof(ifmgd->vht_capa_mask));
+
        if (req->ie && req->ie_len) {
                memcpy(assoc_data->ie, req->ie, req->ie_len);
                assoc_data->ie_len = req->ie_len;
@@ -4149,7 +4214,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        rcu_read_unlock();
 
        if (bss->wmm_used && bss->uapsd_supported &&
-           (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
+           (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) &&
+           sdata->wmm_acm != 0xff) {
                assoc_data->uapsd = true;
                ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
        } else {