]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'wireless-next/master' into mac80211-next
authorJohannes Berg <johannes.berg@intel.com>
Mon, 22 Apr 2013 13:31:43 +0000 (15:31 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 22 Apr 2013 13:31:43 +0000 (15:31 +0200)
1  2 
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/rxon.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/wireless/nl80211.c

index cb5882ea5f3afd92e081f27386d84fbfc2b5a0ca,5bc995a485199e3c060ac3cc6c4c0abcdb66ceb0..431ae6cc5f8fd2f95a0f20f58a067d455d281334
@@@ -612,7 -612,7 +612,7 @@@ il4965_pass_packet_to_mac80211(struct i
  
  /* Called for N_RX (legacy ABG frames), or
   * N_RX_MPDU (HT high-throughput N frames). */
- void
static void
  il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
  {
        struct ieee80211_hdr *header;
  
  /* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY).
   * This will be used later in il_hdl_rx() for N_RX_MPDU. */
- void
static void
  il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
  {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@@ -1250,7 -1250,7 +1250,7 @@@ il4965_dump_fh(struct il_priv *il, cha
        return 0;
  }
  
- void
static void
  il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb)
  {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@@ -1357,7 -1357,7 +1357,7 @@@ il4965_accumulative_stats(struct il_pri
  }
  #endif
  
- void
static void
  il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb)
  {
        const int recalib_seconds = 60;
                il4965_temperature_calib(il);
  }
  
- void
static void
  il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb)
  {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@@ -2050,7 -2050,7 +2050,7 @@@ il4965_txq_ctx_reset(struct il_priv *il
                il_tx_queue_reset(il, txq_id);
  }
  
- void
static void
  il4965_txq_ctx_unmap(struct il_priv *il)
  {
        int txq_id;
@@@ -2896,7 -2896,7 +2896,7 @@@ il4965_hwrate_to_tx_control(struct il_p
   * Handles block-acknowledge notification from device, which reports success
   * of frames sent via aggregation.
   */
- void
static void
  il4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb)
  {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
@@@ -6057,7 -6057,7 +6057,7 @@@ il4965_mac_channel_switch(struct ieee80
        struct il_priv *il = hw->priv;
        const struct il_channel_info *ch_info;
        struct ieee80211_conf *conf = &hw->conf;
 -      struct ieee80211_channel *channel = ch_switch->channel;
 +      struct ieee80211_channel *channel = ch_switch->chandef.chan;
        struct il_ht_config *ht_conf = &il->current_ht_config;
        u16 ch;
  
        il->current_ht_config.smps = conf->smps_mode;
  
        /* Configure HT40 channels */
 -      il->ht.enabled = conf_is_ht(conf);
 -      if (il->ht.enabled) {
 -              if (conf_is_ht40_minus(conf)) {
 -                      il->ht.extension_chan_offset =
 -                          IEEE80211_HT_PARAM_CHA_SEC_BELOW;
 -                      il->ht.is_40mhz = true;
 -              } else if (conf_is_ht40_plus(conf)) {
 -                      il->ht.extension_chan_offset =
 -                          IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
 -                      il->ht.is_40mhz = true;
 -              } else {
 -                      il->ht.extension_chan_offset =
 -                          IEEE80211_HT_PARAM_CHA_SEC_NONE;
 -                      il->ht.is_40mhz = false;
 -              }
 -      } else
 +      switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
 +      case NL80211_CHAN_NO_HT:
 +      case NL80211_CHAN_HT20:
                il->ht.is_40mhz = false;
 +              il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
 +              break;
 +      case NL80211_CHAN_HT40MINUS:
 +              il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
 +              il->ht.is_40mhz = true;
 +              break;
 +      case NL80211_CHAN_HT40PLUS:
 +              il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
 +              il->ht.is_40mhz = true;
 +              break;
 +      }
  
        if ((le16_to_cpu(il->staging.channel) != ch))
                il->staging.flags = 0;
@@@ -6315,7 -6317,7 +6315,7 @@@ il4965_tx_queue_set_status(struct il_pr
               scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
  }
  
- const struct ieee80211_ops il4965_mac_ops = {
static const struct ieee80211_ops il4965_mac_ops = {
        .tx = il4965_mac_tx,
        .start = il4965_mac_start,
        .stop = il4965_mac_stop,
index 2dc101fe0d24f48123037e683313b4ee8f452de0,fc3879804622439d20bbf2b791568d71c70de0d9..cab23af0be9e604051e0c2a5d4b8b0a87073a7ca
@@@ -777,9 -777,12 +777,12 @@@ static int iwlagn_mac_ampdu_action(stru
                IWL_DEBUG_HT(priv, "start Tx\n");
                ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
                break;
-       case IEEE80211_AMPDU_TX_STOP_CONT:
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+               IWL_DEBUG_HT(priv, "Flush Tx\n");
+               ret = iwlagn_tx_agg_flush(priv, vif, sta, tid);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_CONT:
                IWL_DEBUG_HT(priv, "stop Tx\n");
                ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
                if ((ret == 0) && (priv->agg_tids_count > 0)) {
@@@ -967,7 -970,7 +970,7 @@@ static void iwlagn_mac_channel_switch(s
  {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
        struct ieee80211_conf *conf = &hw->conf;
 -      struct ieee80211_channel *channel = ch_switch->channel;
 +      struct ieee80211_channel *channel = ch_switch->chandef.chan;
        struct iwl_ht_config *ht_conf = &priv->current_ht_config;
        /*
         * MULTI-FIXME
        priv->current_ht_config.smps = conf->smps_mode;
  
        /* Configure HT40 channels */
 -      ctx->ht.enabled = conf_is_ht(conf);
 -      if (ctx->ht.enabled)
 -              iwlagn_config_ht40(conf, ctx);
 -      else
 +      switch (cfg80211_get_chandef_type(&ch_switch->chandef)) {
 +      case NL80211_CHAN_NO_HT:
 +      case NL80211_CHAN_HT20:
                ctx->ht.is_40mhz = false;
 +              ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
 +              break;
 +      case NL80211_CHAN_HT40MINUS:
 +              ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
 +              ctx->ht.is_40mhz = true;
 +              break;
 +      case NL80211_CHAN_HT40PLUS:
 +              ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
 +              ctx->ht.is_40mhz = true;
 +              break;
 +      }
  
        if ((le16_to_cpu(ctx->staging.channel) != ch))
                ctx->staging.flags = 0;
@@@ -1132,7 -1125,7 +1135,7 @@@ static void iwlagn_mac_flush(struct iee
         */
        if (drop) {
                IWL_DEBUG_MAC80211(priv, "send flush command\n");
-               if (iwlagn_txfifo_flush(priv)) {
+               if (iwlagn_txfifo_flush(priv, 0)) {
                        IWL_ERR(priv, "flush request fail\n");
                        goto done;
                }
index acbb50b5f1e8c36202aea3ebf327101b301f2980,2a349004f18d765fa5723ef3942e5abcbb38c180..707446fa00bdbaa969bdd6f60f3e6dab3d8db3bd
@@@ -1160,7 -1160,7 +1160,7 @@@ int iwlagn_commit_rxon(struct iwl_priv 
  }
  
  void iwlagn_config_ht40(struct ieee80211_conf *conf,
 -      struct iwl_rxon_context *ctx)
 +                      struct iwl_rxon_context *ctx)
  {
        if (conf_is_ht40_minus(conf)) {
                ctx->ht.extension_chan_offset =
@@@ -1420,6 -1420,14 +1420,14 @@@ void iwlagn_bss_info_changed(struct iee
  
        mutex_lock(&priv->mutex);
  
+       if (changes & BSS_CHANGED_IDLE && bss_conf->idle) {
+               /*
+                * If we go idle, then clearly no "passive-no-rx"
+                * workaround is needed any more, this is a reset.
+                */
+               iwlagn_lift_passive_no_rx(priv);
+       }
        if (unlikely(!iwl_is_ready(priv))) {
                IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
                mutex_unlock(&priv->mutex);
                        priv->timestamp = bss_conf->sync_tsf;
                        ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
                } else {
-                       /*
-                        * If we disassociate while there are pending
-                        * frames, just wake up the queues and let the
-                        * frames "escape" ... This shouldn't really
-                        * be happening to start with, but we should
-                        * not get stuck in this case either since it
-                        * can happen if userspace gets confused.
-                        */
-                       iwlagn_lift_passive_no_rx(priv);
                        ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
  
                        if (ctx->ctxid == IWL_RXON_CTX_BSS)
diff --combined net/mac80211/cfg.c
index 72ab1c0e3ca72d3b4dab9a4b77fd4e4f1ea2cbf2,764dd9a6a072a0bc8392ea4a2196a344e30c487b..490990e3fc3888cb370848b0feb7580fca40c0c3
@@@ -1052,7 -1052,6 +1052,7 @@@ static int ieee80211_stop_ap(struct wip
        ieee80211_free_keys(sdata);
  
        sdata->vif.bss_conf.enable_beacon = false;
 +      sdata->vif.bss_conf.ssid_len = 0;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
  
@@@ -2417,22 -2416,9 +2417,22 @@@ static int ieee80211_set_bitrate_mask(s
        }
  
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
 +              struct ieee80211_supported_band *sband = wiphy->bands[i];
 +              int j;
 +
                sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
                memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs,
                       sizeof(mask->control[i].mcs));
 +
 +              sdata->rc_has_mcs_mask[i] = false;
 +              if (!sband)
 +                      continue;
 +
 +              for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++)
 +                      if (~sdata->rc_rateidx_mcs_mask[i][j]) {
 +                              sdata->rc_has_mcs_mask[i] = true;
 +                              break;
 +                      }
        }
  
        return 0;
@@@ -2661,7 -2647,7 +2661,7 @@@ static int ieee80211_cancel_roc(struct 
                        list_del(&dep->list);
                        mutex_unlock(&local->mtx);
  
-                       ieee80211_roc_notify_destroy(dep);
+                       ieee80211_roc_notify_destroy(dep, true);
                        return 0;
                }
  
                        ieee80211_start_next_roc(local);
                mutex_unlock(&local->mtx);
  
-               ieee80211_roc_notify_destroy(found);
+               ieee80211_roc_notify_destroy(found, true);
        } else {
                /* work may be pending so use it all the time */
                found->abort = true;
  
                /* work will clean up etc */
                flush_delayed_work(&found->work);
+               WARN_ON(!found->to_be_freed);
+               kfree(found);
        }
  
        return 0;
diff --combined net/mac80211/chan.c
index 166165efd8e223fea88f49cd341fafc0117e4690,7d0baa89c784888926b98d2a84fc329f04c75122..03e8d2e3270e23f0e97a58595513b33fcb048cb3
@@@ -57,28 -57,13 +57,29 @@@ ieee80211_find_chanctx(struct ieee80211
        return NULL;
  }
  
 +static bool ieee80211_is_radar_required(struct ieee80211_local *local)
 +{
 +      struct ieee80211_sub_if_data *sdata;
 +
 +      rcu_read_lock();
 +      list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 +              if (sdata->radar_required) {
 +                      rcu_read_unlock();
 +                      return true;
 +              }
 +      }
 +      rcu_read_unlock();
 +
 +      return false;
 +}
 +
  static struct ieee80211_chanctx *
  ieee80211_new_chanctx(struct ieee80211_local *local,
                      const struct cfg80211_chan_def *chandef,
                      enum ieee80211_chanctx_mode mode)
  {
        struct ieee80211_chanctx *ctx;
+       u32 changed;
        int err;
  
        lockdep_assert_held(&local->chanctx_mtx);
        ctx->conf.rx_chains_static = 1;
        ctx->conf.rx_chains_dynamic = 1;
        ctx->mode = mode;
 +      ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
 +      if (!local->use_chanctx)
 +              local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
  
+       /* acquire mutex to prevent idle from changing */
+       mutex_lock(&local->mtx);
+       /* turn idle off *before* setting channel -- some drivers need that */
+       changed = ieee80211_idle_off(local);
+       if (changed)
+               ieee80211_hw_config(local, changed);
        if (!local->use_chanctx) {
                local->_oper_chandef = *chandef;
                ieee80211_hw_config(local, 0);
                err = drv_add_chanctx(local, ctx);
                if (err) {
                        kfree(ctx);
-                       return ERR_PTR(err);
+                       ctx = ERR_PTR(err);
+                       ieee80211_recalc_idle(local);
+                       goto out;
                }
        }
  
+       /* and keep the mutex held until the new chanctx is on the list */
        list_add_rcu(&ctx->list, &local->chanctx_list);
  
-       mutex_lock(&local->mtx);
-       ieee80211_recalc_idle(local);
+  out:
        mutex_unlock(&local->mtx);
  
        return ctx;
  static void ieee80211_free_chanctx(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *ctx)
  {
 +      bool check_single_channel = false;
        lockdep_assert_held(&local->chanctx_mtx);
  
        WARN_ON_ONCE(ctx->refcount != 0);
                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
                chandef->center_freq1 = chandef->chan->center_freq;
                chandef->center_freq2 = 0;
 +
 +              /* NOTE: Disabling radar is only valid here for
 +               * single channel context. To be sure, check it ...
 +               */
 +              if (local->hw.conf.radar_enabled)
 +                      check_single_channel = true;
 +              local->hw.conf.radar_enabled = false;
 +
                ieee80211_hw_config(local, 0);
        } else {
                drv_remove_chanctx(local, ctx);
        list_del_rcu(&ctx->list);
        kfree_rcu(ctx, rcu_head);
  
 +      /* throw a warning if this wasn't the only channel context. */
 +      WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
 +
        mutex_lock(&local->mtx);
        ieee80211_recalc_idle(local);
        mutex_unlock(&local->mtx);
@@@ -258,11 -238,19 +269,11 @@@ static void __ieee80211_vif_release_cha
  void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
                                    struct ieee80211_chanctx *chanctx)
  {
 -      struct ieee80211_sub_if_data *sdata;
 -      bool radar_enabled = false;
 +      bool radar_enabled;
  
        lockdep_assert_held(&local->chanctx_mtx);
  
 -      rcu_read_lock();
 -      list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 -              if (sdata->radar_required) {
 -                      radar_enabled = true;
 -                      break;
 -              }
 -      }
 -      rcu_read_unlock();
 +      radar_enabled = ieee80211_is_radar_required(local);
  
        if (radar_enabled == chanctx->conf.radar_enabled)
                return;
index 21c1720eee000c6861fe48ac255afd21068c0caf,135ab463cfd99043db705d83dc49a970951f3fb5..af8410e1291e90f9f5f52da268761d729970a5dd
@@@ -309,6 -309,7 +309,7 @@@ struct ieee80211_roc_work 
        struct ieee80211_channel *chan;
  
        bool started, abort, hw_begun, notified;
+       bool to_be_freed;
  
        unsigned long hw_start_time;
  
@@@ -739,8 -740,6 +740,8 @@@ struct ieee80211_sub_if_data 
  
        /* bitmap of allowed (non-MCS) rate indexes for rate control */
        u32 rc_rateidx_mask[IEEE80211_NUM_BANDS];
 +
 +      bool rc_has_mcs_mask[IEEE80211_NUM_BANDS];
        u8  rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];
  
        union {
@@@ -1021,7 -1020,7 +1022,7 @@@ struct ieee80211_local 
        enum mac80211_scan_state next_scan_state;
        struct delayed_work scan_work;
        struct ieee80211_sub_if_data __rcu *scan_sdata;
 -      struct ieee80211_channel *csa_channel;
 +      struct cfg80211_chan_def csa_chandef;
        /* For backward compatibility only -- do not use */
        struct cfg80211_chan_def _oper_chandef;
  
@@@ -1180,13 -1179,10 +1181,13 @@@ struct ieee802_11_elems 
        const u8 *perr;
        const struct ieee80211_rann_ie *rann;
        const struct ieee80211_channel_sw_ie *ch_switch_ie;
 +      const struct ieee80211_ext_chansw_ie *ext_chansw_ie;
 +      const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
        const u8 *country_elem;
        const u8 *pwr_constr_elem;
        const struct ieee80211_timeout_interval_ie *timeout_int;
        const u8 *opmode_notif;
 +      const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
  
        /* length of them, respectively */
        u8 ssid_len;
@@@ -1257,6 -1253,10 +1258,6 @@@ void ieee80211_recalc_ps_vif(struct iee
  int ieee80211_max_network_latency(struct notifier_block *nb,
                                  unsigned long data, void *dummy);
  int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata);
 -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);
  void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata);
  void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                  struct sk_buff *skb);
@@@ -1318,7 -1318,7 +1319,7 @@@ void ieee80211_roc_setup(struct ieee802
  void ieee80211_start_next_roc(struct ieee80211_local *local);
  void ieee80211_roc_purge(struct ieee80211_local *local,
                         struct ieee80211_sub_if_data *sdata);
- void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc);
+ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
  void ieee80211_sw_roc_work(struct work_struct *work);
  void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
  
@@@ -1332,6 -1332,7 +1333,7 @@@ int ieee80211_if_change_type(struct iee
                             enum nl80211_iftype type);
  void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
  void ieee80211_remove_interfaces(struct ieee80211_local *local);
+ u32 ieee80211_idle_off(struct ieee80211_local *local);
  void ieee80211_recalc_idle(struct ieee80211_local *local);
  void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
                                    const int offset);
@@@ -1493,13 -1494,13 +1495,13 @@@ static inline void ieee80211_tx_skb(str
        ieee80211_tx_skb_tid(sdata, skb, 7);
  }
  
 -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
 +u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action,
                               struct ieee802_11_elems *elems,
                               u64 filter, u32 crc);
 -static inline void ieee802_11_parse_elems(u8 *start, size_t len,
 +static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action,
                                          struct ieee802_11_elems *elems)
  {
 -      ieee802_11_parse_elems_crc(start, len, elems, 0, 0);
 +      ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
  }
  
  u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
diff --combined net/mac80211/mesh.c
index 4b984765d62db5b07119cbf854d5d2afcd946ad4,fd1024ef393b1518bf71f348771b7834374ceb4a..6952760881c8f25fafe15efd03fe4dbd3eb443a1
@@@ -838,7 -838,7 +838,7 @@@ ieee80211_mesh_rx_probe_req(struct ieee
        if (baselen > len)
                return;
  
 -      ieee802_11_parse_elems(pos, len - baselen, &elems);
 +      ieee802_11_parse_elems(pos, len - baselen, false, &elems);
  
        /* 802.11-2012 10.1.4.3.2 */
        if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
@@@ -899,7 -899,7 +899,7 @@@ static void ieee80211_mesh_rx_bcn_presp
                return;
  
        ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
 -                             &elems);
 +                             false, &elems);
  
        /* ignore non-mesh or secure / unsecure mismatch */
        if ((!elems.mesh_id || !elems.mesh_config) ||
@@@ -1004,7 -1004,8 +1004,8 @@@ void ieee80211_mesh_notify_scan_complet
  
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &local->interfaces, list)
-               if (ieee80211_vif_is_mesh(&sdata->vif))
+               if (ieee80211_vif_is_mesh(&sdata->vif) &&
+                   ieee80211_sdata_running(sdata))
                        ieee80211_queue_work(&local->hw, &sdata->work);
        rcu_read_unlock();
  }
diff --combined net/mac80211/mlme.c
index c7860d0450dd17fe71d74d97648aaeac75d5be48,9c4968938472683c6e07d1f35c9ec396720563f8..f7beb12abde23b05a0a932bc6fe7726c7014df99
@@@ -289,8 -289,6 +289,8 @@@ ieee80211_determine_chantype(struct iee
        } 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;
        }
  
                                               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;
                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,6 -604,7 +604,6 @@@ static void ieee80211_add_vht_ie(struc
        u8 *pos;
        u32 cap;
        struct ieee80211_sta_vht_cap vht_cap;
 -      int i;
  
        BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
  
                        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);
@@@ -966,7 -998,16 +966,7 @@@ static void ieee80211_chswitch_work(str
        if (!ifmgd->associated)
                goto out;
  
 -      /*
 -       * FIXME: Here we are downgrading to NL80211_CHAN_WIDTH_20_NOHT
 -       * and don't adjust our ht/vht settings
 -       * This is wrong - we should behave according to the CSA params
 -       */
 -      local->_oper_chandef.chan = local->csa_channel;
 -      local->_oper_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
 -      local->_oper_chandef.center_freq1 =
 -              local->_oper_chandef.chan->center_freq;
 -      local->_oper_chandef.center_freq2 = 0;
 +      local->_oper_chandef = local->csa_chandef;
  
        if (!local->ops->channel_switch) {
                /* call "hw_config" only if doing sw channel switch */
@@@ -1013,193 -1054,56 +1013,193 @@@ static void ieee80211_chswitch_timer(un
        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),
        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,
@@@ -2215,6 -2120,7 +2215,6 @@@ void ieee80211_beacon_loss(struct ieee8
  
        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);
  }
@@@ -2264,7 -2170,7 +2264,7 @@@ static void ieee80211_auth_challenge(st
        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;
@@@ -2529,7 -2435,7 +2529,7 @@@ static bool ieee80211_assoc_success(str
        }
  
        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");
@@@ -2698,7 -2604,7 +2698,7 @@@ ieee80211_rx_mgmt_assoc_resp(struct iee
                   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 &&
@@@ -2753,8 -2659,6 +2753,8 @@@ static void ieee80211_rx_bss_info(struc
        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 &&
        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) {
                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);
 +
  }
  
  
@@@ -2821,7 -2726,7 +2821,7 @@@ static void ieee80211_rx_mgmt_probe_res
                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);
  
@@@ -2904,7 -2809,7 +2904,7 @@@ ieee80211_rx_mgmt_beacon(struct ieee802
        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;
  
        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) {
@@@ -3161,8 -3066,6 +3161,8 @@@ void ieee80211_sta_rx_queued_mgmt(struc
        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;
                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);
  
@@@ -3665,8 -3534,10 +3665,10 @@@ void ieee80211_mlme_notify_scan_complet
  
        /* Restart STA timers */
        rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list)
-               ieee80211_restart_sta_timer(sdata);
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (ieee80211_sdata_running(sdata))
+                       ieee80211_restart_sta_timer(sdata);
+       }
        rcu_read_unlock();
  }
  
diff --combined net/mac80211/rx.c
index 14b32a4cd7bb579b35c2ebd1b232011d0271d7aa,2528b5a4d6d42b208e29287bbf3458a3554a0af8..c8447af76ead2d598fc08c82860b64079e8c0d22
@@@ -2085,7 -2085,6 +2085,7 @@@ ieee80211_rx_h_mesh_fwding(struct ieee8
        }
  
        fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data;
 +      fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
        info = IEEE80211_SKB_CB(fwd_skb);
        memset(info, 0, sizeof(*info));
        info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
@@@ -2424,22 -2423,6 +2424,22 @@@ ieee80211_rx_h_action(struct ieee80211_
                }
  
                break;
 +      case WLAN_CATEGORY_PUBLIC:
 +              if (len < IEEE80211_MIN_ACTION_SIZE + 1)
 +                      goto invalid;
 +              if (sdata->vif.type != NL80211_IFTYPE_STATION)
 +                      break;
 +              if (!rx->sta)
 +                      break;
 +              if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid))
 +                      break;
 +              if (mgmt->u.action.u.ext_chan_switch.action_code !=
 +                              WLAN_PUB_ACTION_EXT_CHANSW_ANN)
 +                      break;
 +              if (len < offsetof(struct ieee80211_mgmt,
 +                                 u.action.u.ext_chan_switch.variable))
 +                      goto invalid;
 +              goto queue;
        case WLAN_CATEGORY_VHT:
                if (sdata->vif.type != NL80211_IFTYPE_STATION &&
                    sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
                        ieee80211_process_measurement_req(sdata, mgmt, len);
                        goto handled;
                case WLAN_ACTION_SPCT_CHL_SWITCH:
 -                      if (len < (IEEE80211_MIN_ACTION_SIZE +
 -                                 sizeof(mgmt->u.action.u.chan_switch)))
 -                              break;
 -
                        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                                break;
  
@@@ -2679,7 -2666,19 +2679,19 @@@ ieee80211_rx_h_action_return(struct iee
  
                memset(nskb->cb, 0, sizeof(nskb->cb));
  
-               ieee80211_tx_skb(rx->sdata, nskb);
+               if (rx->sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) {
+                       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(nskb);
+                       info->flags = IEEE80211_TX_CTL_TX_OFFCHAN |
+                                     IEEE80211_TX_INTFL_OFFCHAN_TX_OK |
+                                     IEEE80211_TX_CTL_NO_CCK_RATE;
+                       if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+                               info->hw_queue =
+                                       local->hw.offchannel_tx_hw_queue;
+               }
+               __ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7,
+                                           status->band);
        }
        dev_kfree_skb(rx->skb);
        return RX_QUEUED;
@@@ -3043,8 -3042,7 +3055,8 @@@ static int prepare_for_handlers(struct 
                    !ieee80211_is_probe_resp(hdr->frame_control) &&
                    !ieee80211_is_beacon(hdr->frame_control))
                        return 0;
 -              if (!ether_addr_equal(sdata->vif.addr, hdr->addr1))
 +              if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) &&
 +                  !multicast)
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                break;
        default:
diff --combined net/wireless/nl80211.c
index 8c8a57937b224093f8086ca830c96800d6f516ea,671b69a3c13646e39e4fc00209b4d588f1d37a1a..212d2aa7a1c58b6673d9ebdc63bb7373f5d713db
@@@ -447,69 -447,62 +447,69 @@@ nl80211_match_policy[NL80211_SCHED_SCAN
        [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
  };
  
 -/* ifidx get helper */
 -static int nl80211_get_ifidx(struct netlink_callback *cb)
 +static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
 +                                   struct netlink_callback *cb,
 +                                   struct cfg80211_registered_device **rdev,
 +                                   struct wireless_dev **wdev)
  {
 -      int res;
 -
 -      res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
 -                        nl80211_fam.attrbuf, nl80211_fam.maxattr,
 -                        nl80211_policy);
 -      if (res)
 -              return res;
 -
 -      if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
 -              return -EINVAL;
 +      int err;
  
 -      res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
 -      if (!res)
 -              return -EINVAL;
 -      return res;
 -}
 +      rtnl_lock();
 +      mutex_lock(&cfg80211_mutex);
  
 -static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
 -                                     struct netlink_callback *cb,
 -                                     struct cfg80211_registered_device **rdev,
 -                                     struct net_device **dev)
 -{
 -      int ifidx = cb->args[0];
 -      int err;
 +      if (!cb->args[0]) {
 +              err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
 +                                nl80211_fam.attrbuf, nl80211_fam.maxattr,
 +                                nl80211_policy);
 +              if (err)
 +                      goto out_unlock;
  
 -      if (!ifidx)
 -              ifidx = nl80211_get_ifidx(cb);
 -      if (ifidx < 0)
 -              return ifidx;
 +              *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
 +                                                 nl80211_fam.attrbuf);
 +              if (IS_ERR(*wdev)) {
 +                      err = PTR_ERR(*wdev);
 +                      goto out_unlock;
 +              }
 +              *rdev = wiphy_to_dev((*wdev)->wiphy);
 +              cb->args[0] = (*rdev)->wiphy_idx;
 +              cb->args[1] = (*wdev)->identifier;
 +      } else {
 +              struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0]);
 +              struct wireless_dev *tmp;
  
 -      cb->args[0] = ifidx;
 +              if (!wiphy) {
 +                      err = -ENODEV;
 +                      goto out_unlock;
 +              }
 +              *rdev = wiphy_to_dev(wiphy);
 +              *wdev = NULL;
  
 -      rtnl_lock();
 +              mutex_lock(&(*rdev)->devlist_mtx);
 +              list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
 +                      if (tmp->identifier == cb->args[1]) {
 +                              *wdev = tmp;
 +                              break;
 +                      }
 +              }
 +              mutex_unlock(&(*rdev)->devlist_mtx);
  
 -      *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
 -      if (!*dev) {
 -              err = -ENODEV;
 -              goto out_rtnl;
 +              if (!*wdev) {
 +                      err = -ENODEV;
 +                      goto out_unlock;
 +              }
        }
  
 -      *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
 -      if (IS_ERR(*rdev)) {
 -              err = PTR_ERR(*rdev);
 -              goto out_rtnl;
 -      }
 +      cfg80211_lock_rdev(*rdev);
  
 +      mutex_unlock(&cfg80211_mutex);
        return 0;
 - out_rtnl:
 + out_unlock:
 +      mutex_unlock(&cfg80211_mutex);
        rtnl_unlock();
        return err;
  }
  
 -static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
 +static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
  {
        cfg80211_unlock_rdev(rdev);
        rtnl_unlock();
@@@ -3532,20 -3525,15 +3532,20 @@@ static int nl80211_dump_station(struct 
  {
        struct station_info sinfo;
        struct cfg80211_registered_device *dev;
 -      struct net_device *netdev;
 +      struct wireless_dev *wdev;
        u8 mac_addr[ETH_ALEN];
 -      int sta_idx = cb->args[1];
 +      int sta_idx = cb->args[2];
        int err;
  
 -      err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
 +      err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
        if (err)
                return err;
  
 +      if (!wdev->netdev) {
 +              err = -EINVAL;
 +              goto out_err;
 +      }
 +
        if (!dev->ops->dump_station) {
                err = -EOPNOTSUPP;
                goto out_err;
  
        while (1) {
                memset(&sinfo, 0, sizeof(sinfo));
 -              err = rdev_dump_station(dev, netdev, sta_idx,
 +              err = rdev_dump_station(dev, wdev->netdev, sta_idx,
                                        mac_addr, &sinfo);
                if (err == -ENOENT)
                        break;
                if (nl80211_send_station(skb,
                                NETLINK_CB(cb->skb).portid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
 -                              dev, netdev, mac_addr,
 +                              dev, wdev->netdev, mac_addr,
                                &sinfo) < 0)
                        goto out;
  
  
  
   out:
 -      cb->args[1] = sta_idx;
 +      cb->args[2] = sta_idx;
        err = skb->len;
   out_err:
 -      nl80211_finish_netdev_dump(dev);
 +      nl80211_finish_wdev_dump(dev);
  
        return err;
  }
@@@ -4179,13 -4167,13 +4179,13 @@@ static int nl80211_dump_mpath(struct sk
  {
        struct mpath_info pinfo;
        struct cfg80211_registered_device *dev;
 -      struct net_device *netdev;
 +      struct wireless_dev *wdev;
        u8 dst[ETH_ALEN];
        u8 next_hop[ETH_ALEN];
 -      int path_idx = cb->args[1];
 +      int path_idx = cb->args[2];
        int err;
  
 -      err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
 +      err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
        if (err)
                return err;
  
                goto out_err;
        }
  
 -      if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
 +      if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
                err = -EOPNOTSUPP;
                goto out_err;
        }
  
        while (1) {
 -              err = rdev_dump_mpath(dev, netdev, path_idx, dst, next_hop,
 -                                    &pinfo);
 +              err = rdev_dump_mpath(dev, wdev->netdev, path_idx, dst,
 +                                    next_hop, &pinfo);
                if (err == -ENOENT)
                        break;
                if (err)
  
                if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
                                       cb->nlh->nlmsg_seq, NLM_F_MULTI,
 -                                     netdev, dst, next_hop,
 +                                     wdev->netdev, dst, next_hop,
                                       &pinfo) < 0)
                        goto out;
  
  
  
   out:
 -      cb->args[1] = path_idx;
 +      cb->args[2] = path_idx;
        err = skb->len;
   out_err:
 -      nl80211_finish_netdev_dump(dev);
 +      nl80211_finish_wdev_dump(dev);
        return err;
  }
  
@@@ -5060,14 -5048,19 +5060,19 @@@ static int nl80211_trigger_scan(struct 
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
  
-       if (rdev->scan_req)
-               return -EBUSY;
+       mutex_lock(&rdev->sched_scan_mtx);
+       if (rdev->scan_req) {
+               err = -EBUSY;
+               goto unlock;
+       }
  
        if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                n_channels = validate_scan_freqs(
                                info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
-               if (!n_channels)
-                       return -EINVAL;
+               if (!n_channels) {
+                       err = -EINVAL;
+                       goto unlock;
+               }
        } else {
                enum ieee80211_band band;
                n_channels = 0;
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
                        n_ssids++;
  
-       if (n_ssids > wiphy->max_scan_ssids)
-               return -EINVAL;
+       if (n_ssids > wiphy->max_scan_ssids) {
+               err = -EINVAL;
+               goto unlock;
+       }
  
        if (info->attrs[NL80211_ATTR_IE])
                ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        else
                ie_len = 0;
  
-       if (ie_len > wiphy->max_scan_ie_len)
-               return -EINVAL;
+       if (ie_len > wiphy->max_scan_ie_len) {
+               err = -EINVAL;
+               goto unlock;
+       }
  
        request = kzalloc(sizeof(*request)
                        + sizeof(*request->ssids) * n_ssids
                        + sizeof(*request->channels) * n_channels
                        + ie_len, GFP_KERNEL);
-       if (!request)
-               return -ENOMEM;
+       if (!request) {
+               err = -ENOMEM;
+               goto unlock;
+       }
  
        if (n_ssids)
                request->ssids = (void *)&request->channels[n_channels];
                kfree(request);
        }
  
+  unlock:
+       mutex_unlock(&rdev->sched_scan_mtx);
        return err;
  }
  
@@@ -5564,13 -5565,9 +5577,13 @@@ static int nl80211_send_bss(struct sk_b
  
        genl_dump_check_consistent(cb, hdr, &nl80211_fam);
  
 -      if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation) ||
 +      if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation))
 +              goto nla_put_failure;
 +      if (wdev->netdev &&
            nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
                goto nla_put_failure;
 +      if (nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
 +              goto nla_put_failure;
  
        bss = nla_nest_start(msg, NL80211_ATTR_BSS);
        if (!bss)
        return -EMSGSIZE;
  }
  
 -static int nl80211_dump_scan(struct sk_buff *skb,
 -                           struct netlink_callback *cb)
 +static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
  {
        struct cfg80211_registered_device *rdev;
 -      struct net_device *dev;
        struct cfg80211_internal_bss *scan;
        struct wireless_dev *wdev;
 -      int start = cb->args[1], idx = 0;
 +      int start = cb->args[2], idx = 0;
        int err;
  
 -      err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
 +      err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (err)
                return err;
  
 -      wdev = dev->ieee80211_ptr;
 -
        wdev_lock(wdev);
        spin_lock_bh(&rdev->bss_lock);
        cfg80211_bss_expire(rdev);
        spin_unlock_bh(&rdev->bss_lock);
        wdev_unlock(wdev);
  
 -      cb->args[1] = idx;
 -      nl80211_finish_netdev_dump(rdev);
 +      cb->args[2] = idx;
 +      nl80211_finish_wdev_dump(rdev);
  
        return skb->len;
  }
@@@ -5752,19 -5753,14 +5765,19 @@@ static int nl80211_dump_survey(struct s
  {
        struct survey_info survey;
        struct cfg80211_registered_device *dev;
 -      struct net_device *netdev;
 -      int survey_idx = cb->args[1];
 +      struct wireless_dev *wdev;
 +      int survey_idx = cb->args[2];
        int res;
  
 -      res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
 +      res = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
        if (res)
                return res;
  
 +      if (!wdev->netdev) {
 +              res = -EINVAL;
 +              goto out_err;
 +      }
 +
        if (!dev->ops->dump_survey) {
                res = -EOPNOTSUPP;
                goto out_err;
        while (1) {
                struct ieee80211_channel *chan;
  
 -              res = rdev_dump_survey(dev, netdev, survey_idx, &survey);
 +              res = rdev_dump_survey(dev, wdev->netdev, survey_idx, &survey);
                if (res == -ENOENT)
                        break;
                if (res)
                if (nl80211_send_survey(skb,
                                NETLINK_CB(cb->skb).portid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
 -                              netdev,
 -                              &survey) < 0)
 +                              wdev->netdev, &survey) < 0)
                        goto out;
                survey_idx++;
        }
  
   out:
 -      cb->args[1] = survey_idx;
 +      cb->args[2] = survey_idx;
        res = skb->len;
   out_err:
 -      nl80211_finish_netdev_dump(dev);
 +      nl80211_finish_wdev_dump(dev);
        return res;
  }
  
@@@ -8146,20 -8143,9 +8159,9 @@@ static int nl80211_stop_p2p_device(stru
        if (!rdev->ops->stop_p2p_device)
                return -EOPNOTSUPP;
  
-       if (!wdev->p2p_started)
-               return 0;
-       rdev_stop_p2p_device(rdev, wdev);
-       wdev->p2p_started = false;
-       mutex_lock(&rdev->devlist_mtx);
-       rdev->opencount--;
-       mutex_unlock(&rdev->devlist_mtx);
-       if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) {
-               rdev->scan_req->aborted = true;
-               ___cfg80211_scan_done(rdev, true);
-       }
+       mutex_lock(&rdev->sched_scan_mtx);
+       cfg80211_stop_p2p_device(rdev, wdev);
+       mutex_unlock(&rdev->sched_scan_mtx);
  
        return 0;
  }
@@@ -8945,7 -8931,7 +8947,7 @@@ static int nl80211_add_scan_req(struct 
        struct nlattr *nest;
        int i;
  
-       ASSERT_RDEV_LOCK(rdev);
+       lockdep_assert_held(&rdev->sched_scan_mtx);
  
        if (WARN_ON(!req))
                return 0;