]> Git Repo - linux.git/commitdiff
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
authorJohn W. Linville <[email protected]>
Tue, 5 Nov 2013 20:49:02 +0000 (15:49 -0500)
committerJohn W. Linville <[email protected]>
Tue, 5 Nov 2013 20:49:02 +0000 (15:49 -0500)
Conflicts:
net/wireless/reg.c

1  2 
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/ibss.c
net/wireless/nl80211.c

diff --combined net/mac80211/cfg.c
index b0a651cc389fdab807b2a8b800f63144d0eaeb2f,ed1e9a84fab8fd71395f2b86d76c1081a6b4119d..95667b088c5b73cd0e95e8c1753ed76acec9dca0
@@@ -1059,6 -1059,7 +1059,7 @@@ static int ieee80211_stop_ap(struct wip
        /* abort any running channel switch */
        sdata->vif.csa_active = false;
        cancel_work_sync(&sdata->csa_finalize_work);
+       cancel_work_sync(&sdata->u.ap.request_smps_work);
  
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@@ -1342,8 -1343,8 +1343,8 @@@ static int sta_apply_parameters(struct 
                                sta->plink_state = params->plink_state;
  
                                ieee80211_mps_sta_status_update(sta);
-                               changed |=
-                                     ieee80211_mps_local_status_update(sdata);
+                               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                               NL80211_MESH_POWER_UNKNOWN);
                                break;
                        default:
                                /*  nothing  */
@@@ -1553,6 -1554,20 +1554,20 @@@ static int ieee80211_change_station(str
  
        mutex_unlock(&local->sta_mtx);
  
+       if ((sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
+           sta->known_smps_mode != sta->sdata->bss->req_smps &&
+           test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
+           sta_info_tx_streams(sta) != 1) {
+               ht_dbg(sta->sdata,
+                      "%pM just authorized and MIMO capable - update SMPS\n",
+                      sta->sta.addr);
+               ieee80211_send_smps_action(sta->sdata,
+                       sta->sdata->bss->req_smps,
+                       sta->sta.addr,
+                       sta->sdata->vif.bss_conf.bssid);
+       }
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
                ieee80211_recalc_ps(local, -1);
@@@ -2337,8 -2352,92 +2352,92 @@@ static int ieee80211_testmode_dump(stru
  }
  #endif
  
- int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode)
+ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode)
+ {
+       struct sta_info *sta;
+       enum ieee80211_smps_mode old_req;
+       int i;
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
+               return -EINVAL;
+       if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
+               return 0;
+       old_req = sdata->u.ap.req_smps;
+       sdata->u.ap.req_smps = smps_mode;
+       /* AUTOMATIC doesn't mean much for AP - don't allow it */
+       if (old_req == smps_mode ||
+           smps_mode == IEEE80211_SMPS_AUTOMATIC)
+               return 0;
+        /* If no associated stations, there's no need to do anything */
+       if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
+               sdata->smps_mode = smps_mode;
+               ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+               return 0;
+       }
+       ht_dbg(sdata,
+              "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
+              smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
+       mutex_lock(&sdata->local->sta_mtx);
+       for (i = 0; i < STA_HASH_SIZE; i++) {
+               for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
+                               lockdep_is_held(&sdata->local->sta_mtx));
+                    sta;
+                    sta = rcu_dereference_protected(sta->hnext,
+                               lockdep_is_held(&sdata->local->sta_mtx))) {
+                       /*
+                        * Only stations associated to our AP and
+                        * associated VLANs
+                        */
+                       if (sta->sdata->bss != &sdata->u.ap)
+                               continue;
+                       /* This station doesn't support MIMO - skip it */
+                       if (sta_info_tx_streams(sta) == 1)
+                               continue;
+                       /*
+                        * Don't wake up a STA just to send the action frame
+                        * unless we are getting more restrictive.
+                        */
+                       if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
+                           !ieee80211_smps_is_restrictive(sta->known_smps_mode,
+                                                          smps_mode)) {
+                               ht_dbg(sdata,
+                                      "Won't send SMPS to sleeping STA %pM\n",
+                                      sta->sta.addr);
+                               continue;
+                       }
+                       /*
+                        * If the STA is not authorized, wait until it gets
+                        * authorized and the action frame will be sent then.
+                        */
+                       if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                               continue;
+                       ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
+                       ieee80211_send_smps_action(sdata, smps_mode,
+                                                  sta->sta.addr,
+                                                  sdata->vif.bss_conf.bssid);
+               }
+       }
+       mutex_unlock(&sdata->local->sta_mtx);
+       sdata->smps_mode = smps_mode;
+       ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
+       return 0;
+ }
+ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode)
  {
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
  
        lockdep_assert_held(&sdata->wdev.mtx);
  
+       if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
+               return -EINVAL;
        old_req = sdata->u.mgd.req_smps;
        sdata->u.mgd.req_smps = smps_mode;
  
@@@ -2402,7 -2504,7 +2504,7 @@@ static int ieee80211_set_power_mgmt(str
  
        /* no change, but if automatic follow powersave */
        sdata_lock(sdata);
-       __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+       __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
        sdata_unlock(sdata);
  
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
@@@ -2860,7 -2962,7 +2962,7 @@@ void ieee80211_csa_finalize_work(struc
                container_of(work, struct ieee80211_sub_if_data,
                             csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
-       int err, changed;
+       int err, changed = 0;
  
        if (!ieee80211_sdata_running(sdata))
                return;
        case NL80211_IFTYPE_ADHOC:
                ieee80211_ibss_finish_csa(sdata);
                break;
+ #ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               err = ieee80211_mesh_finish_csa(sdata);
+               if (err < 0)
+                       return;
+               break;
+ #endif
        default:
                WARN_ON(1);
                return;
@@@ -2912,6 -3021,7 +3021,7 @@@ static int ieee80211_channel_switch(str
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chanctx *chanctx;
+       struct ieee80211_if_mesh __maybe_unused *ifmsh;
        int err, num_chanctx;
  
        if (!list_empty(&local->roc_list) || local->scanning)
                if (err < 0)
                        return err;
                break;
+ #ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               ifmsh = &sdata->u.mesh;
+               if (!ifmsh->mesh_id)
+                       return -EINVAL;
+               if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
+                       return -EINVAL;
+               /* changes into another band are not supported */
+               if (sdata->vif.bss_conf.chandef.chan->band !=
+                   params->chandef.chan->band)
+                       return -EINVAL;
+               err = ieee80211_mesh_csa_beacon(sdata, params, true);
+               if (err < 0)
+                       return err;
+               break;
+ #endif
        default:
                return -EOPNOTSUPP;
        }
@@@ -3564,7 -3694,7 +3694,7 @@@ static int ieee80211_probe_client(struc
                return -EINVAL;
        }
        band = chanctx_conf->def.chan->band;
 -      sta = sta_info_get(sdata, peer);
 +      sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = test_sta_flag(sta, WLAN_STA_WME);
        } else {
index fe48b093d4dc1cafb420f6e75526f4bf851fa4fe,5cfa160be05b6fa2520425f65bdcbed15ae6c100..29dc505be125c3c19737f8f6cee911492c52727c
@@@ -262,6 -262,10 +262,10 @@@ struct ieee80211_if_ap 
  
        struct ps_data ps;
        atomic_t num_mcast_sta; /* number of stations receiving multicast */
+       enum ieee80211_smps_mode req_smps, /* requested smps mode */
+                        driver_smps_mode; /* smps mode request */
+       struct work_struct request_smps_work;
  };
  
  struct ieee80211_if_wds {
@@@ -334,7 -338,6 +338,7 @@@ enum ieee80211_sta_flags 
        IEEE80211_STA_DISABLE_VHT       = BIT(11),
        IEEE80211_STA_DISABLE_80P80MHZ  = BIT(12),
        IEEE80211_STA_DISABLE_160MHZ    = BIT(13),
 +      IEEE80211_STA_DISABLE_WMM       = BIT(14),
  };
  
  struct ieee80211_mgd_auth_data {
@@@ -498,6 -501,7 +502,7 @@@ struct ieee80211_if_ibss 
        bool privacy;
  
        bool control_port;
+       bool userspace_handles_dfs;
  
        u8 bssid[ETH_ALEN] __aligned(2);
        u8 ssid[IEEE80211_MAX_SSID_LEN];
@@@ -539,6 -543,11 +544,11 @@@ struct ieee80211_mesh_sync_ops 
        /* add other framework functions here */
  };
  
+ struct mesh_csa_settings {
+       struct rcu_head rcu_head;
+       struct cfg80211_csa_settings settings;
+ };
  struct ieee80211_if_mesh {
        struct timer_list housekeeping_timer;
        struct timer_list mesh_path_timer;
        int ps_peers_light_sleep;
        int ps_peers_deep_sleep;
        struct ps_data ps;
+       /* Channel Switching Support */
+       struct mesh_csa_settings __rcu *csa;
+       bool chsw_init;
+       u8 chsw_ttl;
+       u16 pre_value;
  };
  
  #ifdef CONFIG_MAC80211_MESH
@@@ -894,8 -908,6 +909,8 @@@ struct tpt_led_trigger 
   *    that the scan completed.
   * @SCAN_ABORTED: Set for our scan work function when the driver reported
   *    a scan complete for an aborted scan.
 + * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being
 + *    cancelled.
   */
  enum {
        SCAN_SW_SCANNING,
        SCAN_ONCHANNEL_SCANNING,
        SCAN_COMPLETED,
        SCAN_ABORTED,
 +      SCAN_HW_CANCELLED,
  };
  
  /**
@@@ -1207,6 -1218,14 +1222,14 @@@ struct ieee80211_ra_tid 
        u16 tid;
  };
  
+ /* this struct holds the value parsing from channel switch IE  */
+ struct ieee80211_csa_ie {
+       struct cfg80211_chan_def chandef;
+       u8 mode;
+       u8 count;
+       u8 ttl;
+ };
  /* Parsed Information Elements */
  struct ieee802_11_elems {
        const u8 *ie_start;
        const struct ieee80211_timeout_interval_ie *timeout_int;
        const u8 *opmode_notif;
        const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+       const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
  
        /* length of them, respectively */
        u8 ssid_len;
@@@ -1343,6 -1363,10 +1367,10 @@@ void ieee80211_ibss_stop(struct ieee802
  void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
  void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                   struct sk_buff *skb);
+ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings,
+                             bool csa_action);
+ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
  
  /* scan/BSS handling */
  void ieee80211_scan_work(struct work_struct *work);
@@@ -1439,7 -1463,10 +1467,10 @@@ void ieee80211_send_delba(struct ieee80
  int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
                               enum ieee80211_smps_mode smps, const u8 *da,
                               const u8 *bssid);
- void ieee80211_request_smps_work(struct work_struct *work);
+ void ieee80211_request_smps_ap_work(struct work_struct *work);
+ void ieee80211_request_smps_mgd_work(struct work_struct *work);
+ bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new);
  
  void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
@@@ -1501,17 -1528,16 +1532,16 @@@ void ieee80211_process_measurement_req(
   *    %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
   *    %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
   *    %IEEE80211_STA_DISABLE_160MHZ.
-  * @count: to be filled with the counter until the switch (on success only)
   * @bssid: the currently connected bssid (for reporting)
-  * @mode: to be filled with CSA mode (on success only)
-  * @new_chandef: to be filled with destination chandef (on success only)
+  * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
+       All of them will be filled with if success only.
   * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
   */
  int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                                 struct ieee802_11_elems *elems, bool beacon,
                                 enum ieee80211_band current_band,
-                                u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
-                                struct cfg80211_chan_def *new_chandef);
+                                u32 sta_flags, u8 *bssid,
+                                struct ieee80211_csa_ie *csa_ie);
  
  /* Suspend/resume and hw reconfiguration */
  int ieee80211_reconfig(struct ieee80211_local *local);
@@@ -1657,8 -1683,10 +1687,10 @@@ void ieee80211_send_probe_req(struct ie
  u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band, u32 *basic_rates);
- int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
-                            enum ieee80211_smps_mode smps_mode);
+ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
+                                enum ieee80211_smps_mode smps_mode);
+ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
+                               enum ieee80211_smps_mode smps_mode);
  void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
  
  size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
@@@ -1714,6 -1742,8 +1746,8 @@@ void ieee80211_dfs_cac_timer(unsigned l
  void ieee80211_dfs_cac_timer_work(struct work_struct *work);
  void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
  void ieee80211_dfs_radar_detected_work(struct work_struct *work);
+ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings);
  
  #ifdef CONFIG_MAC80211_NOINLINE
  #define debug_noinline noinline
diff --combined net/mac80211/mlme.c
index d7bdc4b97dde2d57d79e2131333ff55f4878c9cb,1305ff984d49771037aa9bf150ff30fc495e7631..d7504ab61a34c7ef6a51f8adce10e58c021408d0
@@@ -958,9 -958,7 +958,7 @@@ ieee80211_sta_process_chanswitch(struc
        struct cfg80211_bss *cbss = ifmgd->associated;
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
-       u8 count;
-       u8 mode;
-       struct cfg80211_chan_def new_chandef = {};
+       struct ieee80211_csa_ie csa_ie;
        int res;
  
        sdata_assert_lock(sdata);
                return;
  
        current_band = cbss->channel->band;
+       memset(&csa_ie, 0, sizeof(csa_ie));
        res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
                                           ifmgd->flags,
-                                          ifmgd->associated->bssid, &count,
-                                          &mode, &new_chandef);
+                                          ifmgd->associated->bssid, &csa_ie);
        if (res < 0)
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
        if (res)
                return;
  
-       if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
+       if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.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_chandef.chan->center_freq,
-                          new_chandef.width, new_chandef.center_freq1,
-                          new_chandef.center_freq2);
+                          csa_ie.chandef.chan->center_freq,
+                          csa_ie.chandef.width, csa_ie.chandef.center_freq1,
+                          csa_ie.chandef.center_freq2);
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                return;
        }
        mutex_unlock(&local->chanctx_mtx);
  
-       local->csa_chandef = new_chandef;
+       local->csa_chandef = csa_ie.chandef;
  
-       if (mode)
+       if (csa_ie.mode)
                ieee80211_stop_queues_by_reason(&local->hw,
                                IEEE80211_MAX_QUEUE_MAP,
                                IEEE80211_QUEUE_STOP_REASON_CSA);
                /* use driver's channel switch callback */
                struct ieee80211_channel_switch ch_switch = {
                        .timestamp = timestamp,
-                       .block_tx = mode,
-                       .chandef = new_chandef,
-                       .count = count,
+                       .block_tx = csa_ie.mode,
+                       .chandef = csa_ie.chandef,
+                       .count = csa_ie.count,
                };
  
                drv_channel_switch(local, &ch_switch);
        }
  
        /* channel switch handled in software */
-       if (count <= 1)
+       if (csa_ie.count <= 1)
                ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(count * cbss->beacon_interval));
+                         TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
  }
  
  static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@@ -2527,7 -2525,7 +2525,7 @@@ static bool ieee80211_assoc_success(str
         */
        ifmgd->wmm_last_param_set = -1;
  
 -      if (elems.wmm_param)
 +      if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
                ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
                                         elems.wmm_param_len);
        else
@@@ -2955,8 -2953,7 +2953,8 @@@ static void ieee80211_rx_mgmt_beacon(st
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
                                         &elems, true);
  
 -      if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
 +      if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
 +          ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
                                     elems.wmm_param_len))
                changed |= BSS_CHANGED_QOS;
  
@@@ -3500,7 -3497,7 +3498,7 @@@ void ieee80211_sta_setup_sdata(struct i
                  ieee80211_beacon_connection_loss_work);
        INIT_WORK(&ifmgd->csa_connection_drop_work,
                  ieee80211_csa_connection_drop_work);
-       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
+       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@@ -3938,44 -3935,6 +3936,44 @@@ int ieee80211_mgd_auth(struct ieee80211
        return err;
  }
  
 +static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
 +                                      const u8 *wmm_param, int len)
 +{
 +      const u8 *pos;
 +      size_t left;
 +
 +      if (len < 8)
 +              return false;
 +
 +      if (wmm_param[5] != 1 /* version */)
 +              return false;
 +
 +      pos = wmm_param + 8;
 +      left = len - 8;
 +
 +      for (; left >= 4; left -= 4, pos += 4) {
 +              u8 aifsn = pos[0] & 0x0f;
 +              u8 ecwmin = pos[1] & 0x0f;
 +              u8 ecwmax = (pos[1] & 0xf0) >> 4;
 +              int aci = (pos[0] >> 5) & 0x03;
 +
 +              if (aifsn < 2) {
 +                      sdata_info(sdata,
 +                                 "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
 +                                 aifsn, aci);
 +                      return false;
 +              }
 +              if (ecwmin > ecwmax) {
 +                      sdata_info(sdata,
 +                                 "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
 +                                 ecwmin, ecwmax, aci);
 +                      return false;
 +              }
 +      }
 +
 +      return true;
 +}
 +
  int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_assoc_request *req)
  {
  
        ifmgd->beacon_crc_valid = false;
  
 +      assoc_data->wmm = bss->wmm_used &&
 +                        (local->hw.queues >= IEEE80211_NUM_ACS);
 +      if (assoc_data->wmm) {
 +              /* try to check validity of WMM params IE */
 +              const struct cfg80211_bss_ies *ies;
 +              const u8 *wp, *start, *end;
 +
 +              rcu_read_lock();
 +              ies = rcu_dereference(req->bss->ies);
 +              start = ies->data;
 +              end = start + ies->len;
 +
 +              while (true) {
 +                      wp = cfg80211_find_vendor_ie(
 +                              WLAN_OUI_MICROSOFT,
 +                              WLAN_OUI_TYPE_MICROSOFT_WMM,
 +                              start, end - start);
 +                      if (!wp)
 +                              break;
 +                      start = wp + wp[1] + 2;
 +                      /* if this IE is too short, try the next */
 +                      if (wp[1] <= 4)
 +                              continue;
 +                      /* if this IE is WMM params, we found what we wanted */
 +                      if (wp[6] == 1)
 +                              break;
 +              }
 +
 +              if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
 +                                                      wp[1] - 2)) {
 +                      assoc_data->wmm = false;
 +                      ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
 +              }
 +              rcu_read_unlock();
 +      }
 +
        /*
         * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
         * We still associate in non-HT mode (11a/b/g) if any one of these
        /* 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 ||
 -          local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
 +          local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
 +          ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
 -              if (!bss->wmm_used)
 +              if (!bss->wmm_used &&
 +                  !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
                        netdev_info(sdata->dev,
                                    "disabling HT as WMM/QoS is not supported by the AP\n");
        }
  
        /* disable VHT if we don't support it or the AP doesn't use WMM */
        if (!sband->vht_cap.vht_supported ||
 -          local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
 +          local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
 +          ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
 -              if (!bss->wmm_used)
 +              if (!bss->wmm_used &&
 +                  !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
                        netdev_info(sdata->dev,
                                    "disabling VHT as WMM/QoS is not supported by the AP\n");
        }
                sdata->smps_mode = ifmgd->req_smps;
  
        assoc_data->capability = req->bss->capability;
 -      assoc_data->wmm = bss->wmm_used &&
 -                        (local->hw.queues >= IEEE80211_NUM_ACS);
        assoc_data->supp_rates = bss->supp_rates;
        assoc_data->supp_rates_len = bss->supp_rates_len;
  
diff --combined net/mac80211/rx.c
index 0011ac8150972470110b004c4780a8dbc8acd406,23f49e8d14c11b4cd72a41a95f6100c864a8cb84..caecef870c0e44e3562cdc0a874bcfd233fc77a1
@@@ -2593,13 -2593,16 +2593,16 @@@ ieee80211_rx_h_action(struct ieee80211_
                                break;
  
                        if (sdata->vif.type != NL80211_IFTYPE_STATION &&
-                           sdata->vif.type != NL80211_IFTYPE_ADHOC)
+                           sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
                                break;
  
                        if (sdata->vif.type == NL80211_IFTYPE_STATION)
                                bssid = sdata->u.mgd.bssid;
                        else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
                                bssid = sdata->u.ibss.bssid;
+                       else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+                               bssid = mgmt->sa;
                        else
                                break;
  
@@@ -3073,9 -3076,6 +3076,9 @@@ static int prepare_for_handlers(struct 
        case NL80211_IFTYPE_ADHOC:
                if (!bssid)
                        return 0;
 +              if (ether_addr_equal(sdata->vif.addr, hdr->addr2) ||
 +                  ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2))
 +                      return 0;
                if (ieee80211_is_beacon(hdr->frame_control)) {
                        return 1;
                } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) {
diff --combined net/mac80211/status.c
index 78dc2e99027e06b8a9fc37f5bdf7f06483476f2b,1ced74c73d2f6592bd221f03dde6d8fe8d1ce909..52a152b01b063b226fb5a523b19a4e9863eeffaf
@@@ -180,9 -180,6 +180,9 @@@ static void ieee80211_frame_acked(struc
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
  
 +      if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
 +              sta->last_rx = jiffies;
 +
        if (ieee80211_is_data_qos(mgmt->frame_control)) {
                struct ieee80211_hdr *hdr = (void *) skb->data;
                u8 *qc = ieee80211_get_qos_ctl(hdr);
        if (ieee80211_is_action(mgmt->frame_control) &&
            mgmt->u.action.category == WLAN_CATEGORY_HT &&
            mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
-           sdata->vif.type == NL80211_IFTYPE_STATION &&
            ieee80211_sdata_running(sdata)) {
-               /*
-                * This update looks racy, but isn't -- if we come
-                * here we've definitely got a station that we're
-                * talking to, and on a managed interface that can
-                * only be the AP. And the only other place updating
-                * this variable in managed mode is before association.
-                */
+               enum ieee80211_smps_mode smps_mode;
                switch (mgmt->u.action.u.ht_smps.smps_control) {
                case WLAN_HT_SMPS_CONTROL_DYNAMIC:
-                       sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_STATIC:
-                       sdata->smps_mode = IEEE80211_SMPS_STATIC;
+                       smps_mode = IEEE80211_SMPS_STATIC;
                        break;
                case WLAN_HT_SMPS_CONTROL_DISABLED:
                default: /* shouldn't happen since we don't send that */
-                       sdata->smps_mode = IEEE80211_SMPS_OFF;
+                       smps_mode = IEEE80211_SMPS_OFF;
                        break;
                }
  
-               ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       /*
+                        * This update looks racy, but isn't -- if we come
+                        * here we've definitely got a station that we're
+                        * talking to, and on a managed interface that can
+                        * only be the AP. And the only other place updating
+                        * this variable in managed mode is before association.
+                        */
+                       sdata->smps_mode = smps_mode;
+                       ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
+               } else if (sdata->vif.type == NL80211_IFTYPE_AP ||
+                          sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+                       sta->known_smps_mode = smps_mode;
+               }
        }
  }
  
diff --combined net/mac80211/tx.c
index 9993fcb19ecdd8b97ea0c1fe1221f916dfd24e7f,9868cb72054e0169659c3abccc231eb8c8e1ded9..c558b246ef0036c38e6c8a00b338b43c46913f78
@@@ -1120,8 -1120,7 +1120,8 @@@ ieee80211_tx_prepare(struct ieee80211_s
                tx->sta = rcu_dereference(sdata->u.vlan.sta);
                if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
                        return TX_DROP;
 -      } else if (info->flags & IEEE80211_TX_CTL_INJECTED ||
 +      } else if (info->flags & (IEEE80211_TX_CTL_INJECTED |
 +                                IEEE80211_TX_INTFL_NL80211_FRAME_TX) ||
                   tx->sdata->control_port_protocol == tx->skb->protocol) {
                tx->sta = sta_info_get_bss(sdata, hdr->addr1);
        }
@@@ -1367,6 -1366,35 +1367,35 @@@ static int invoke_tx_handlers(struct ie
        return 0;
  }
  
+ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, struct sk_buff *skb,
+                             int band, struct ieee80211_sta **sta)
+ {
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_tx_data tx;
+       if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
+               return false;
+       info->band = band;
+       info->control.vif = vif;
+       info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
+       if (invoke_tx_handlers(&tx))
+               return false;
+       if (sta) {
+               if (tx.sta)
+                       *sta = &tx.sta->sta;
+               else
+                       *sta = NULL;
+       }
+       return true;
+ }
+ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
  /*
   * Returns false if the frame couldn't be transmitted but was queued instead.
   */
@@@ -2370,6 -2398,10 +2399,10 @@@ static void ieee80211_update_csa(struc
                beacon_data = beacon->head;
                beacon_data_len = beacon->head_len;
                break;
+       case NL80211_IFTYPE_MESH_POINT:
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+               break;
        default:
                return;
        }
@@@ -2424,6 -2456,15 +2457,15 @@@ bool ieee80211_csa_is_complete(struct i
                if (!beacon)
                        goto out;
  
+               beacon_data = beacon->head;
+               beacon_data_len = beacon->head_len;
+       } else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+               beacon = rcu_dereference(ifmsh->beacon);
+               if (!beacon)
+                       goto out;
                beacon_data = beacon->head;
                beacon_data_len = beacon->head_len;
        } else {
@@@ -2531,6 -2572,9 +2573,9 @@@ struct sk_buff *ieee80211_beacon_get_ti
                if (!bcn)
                        goto out;
  
+               if (sdata->vif.csa_active)
+                       ieee80211_update_csa(sdata, bcn);
                if (ifmsh->sync_ops)
                        ifmsh->sync_ops->adjust_tbtt(
                                                sdata);
diff --combined net/mac80211/util.c
index aefb9d5b962023eb59b750f867de6733eb1c142b,a38d58231af8b9583c37b3aa7c81c74c86469ace..592a18171f95e9ec5273b03307235dff2bd1c946
@@@ -300,9 -300,6 +300,6 @@@ void ieee80211_propagate_queue_wake(str
                if (!sdata->dev)
                        continue;
  
-               if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
-                       continue;
                if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
                    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
                        continue;
@@@ -743,6 -740,7 +740,7 @@@ u32 ieee802_11_parse_elems_crc(const u
                case WLAN_EID_TIMEOUT_INTERVAL:
                case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
+               case WLAN_EID_CHAN_SWITCH_PARAM:
                /*
                 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
                 * that if the content gets bigger it might be needed more than once
                        }
                        elems->sec_chan_offs = (void *)pos;
                        break;
+               case WLAN_EID_CHAN_SWITCH_PARAM:
+                       if (elen !=
+                           sizeof(*elems->mesh_chansw_params_ie)) {
+                               elem_parse_failed = true;
+                               break;
+                       }
+                       elems->mesh_chansw_params_ie = (void *)pos;
+                       break;
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
                        if (!action ||
                            elen != sizeof(*elems->wide_bw_chansw_ie)) {
@@@ -2101,7 -2107,7 +2107,7 @@@ int ieee80211_add_ext_srates_ie(struct 
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
 -      int rate, skip, shift;
 +      int rate, shift;
        u8 i, exrates, *pos;
        u32 basic_rates = sdata->vif.bss_conf.basic_rates;
        u32 rate_flags;
                pos = skb_put(skb, exrates + 2);
                *pos++ = WLAN_EID_EXT_SUPP_RATES;
                *pos++ = exrates;
 -              skip = 0;
                for (i = 8; i < sband->n_bitrates; i++) {
                        u8 basic = 0;
                        if ((rate_flags & sband->bitrates[i].flags)
                            != rate_flags)
                                continue;
 -                      if (skip++ < 8)
 -                              continue;
                        if (need_basic && basic_rates & BIT(i))
                                basic = 0x80;
                        rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
@@@ -2236,10 -2245,6 +2242,10 @@@ u64 ieee80211_calculate_rx_timestamp(st
        }
  
        rate = cfg80211_calculate_bitrate(&ri);
 +      if (WARN_ONCE(!rate,
 +                    "Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
 +                    status->flag, status->rate_idx, status->vht_nss))
 +              return 0;
  
        /* rewind from end of MPDU */
        if (status->flag & RX_FLAG_MACTIME_END)
@@@ -2354,3 -2359,115 +2360,115 @@@ u32 ieee80211_chandef_downgrade(struct 
  
        return ret;
  }
+ /*
+  * Returns true if smps_mode_new is strictly more restrictive than
+  * smps_mode_old.
+  */
+ bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
+                                  enum ieee80211_smps_mode smps_mode_new)
+ {
+       if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
+                        smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
+               return false;
+       switch (smps_mode_old) {
+       case IEEE80211_SMPS_STATIC:
+               return false;
+       case IEEE80211_SMPS_DYNAMIC:
+               return smps_mode_new == IEEE80211_SMPS_STATIC;
+       case IEEE80211_SMPS_OFF:
+               return smps_mode_new != IEEE80211_SMPS_OFF;
+       default:
+               WARN_ON(1);
+       }
+       return false;
+ }
+ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
+                             struct cfg80211_csa_settings *csa_settings)
+ {
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_local *local = sdata->local;
+       int freq;
+       int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
+                              sizeof(mgmt->u.action.u.chan_switch);
+       u8 *pos;
+       if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+           sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+       skb = dev_alloc_skb(local->tx_headroom + hdr_len +
+                           5 + /* channel switch announcement element */
+                           3 + /* secondary channel offset element */
+                           8); /* mesh channel switch parameters element */
+       if (!skb)
+               return -ENOMEM;
+       skb_reserve(skb, local->tx_headroom);
+       mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
+       memset(mgmt, 0, hdr_len);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+       eth_broadcast_addr(mgmt->da);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+       } else {
+               struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+               memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
+       }
+       mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+       mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
+       pos = skb_put(skb, 5);
+       *pos++ = WLAN_EID_CHANNEL_SWITCH;                       /* EID */
+       *pos++ = 3;                                             /* IE length */
+       *pos++ = csa_settings->block_tx ? 1 : 0;                /* CSA mode */
+       freq = csa_settings->chandef.chan->center_freq;
+       *pos++ = ieee80211_frequency_to_channel(freq);          /* channel */
+       *pos++ = csa_settings->count;                           /* count */
+       if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
+               enum nl80211_channel_type ch_type;
+               skb_put(skb, 3);
+               *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;     /* EID */
+               *pos++ = 1;                                     /* IE length */
+               ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
+               if (ch_type == NL80211_CHAN_HT40PLUS)
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+               else
+                       *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+       }
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+               __le16 pre_value;
+               skb_put(skb, 8);
+               *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;            /* EID */
+               *pos++ = 6;                                     /* IE length */
+               *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;     /* Mesh TTL */
+               *pos = 0x00;    /* Mesh Flag: Tx Restrict, Initiator, Reason */
+               *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
+               *pos++ |= csa_settings->block_tx ?
+                         WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
+               put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
+               pos += 2;
+               if (!ifmsh->pre_value)
+                       ifmsh->pre_value = 1;
+               else
+                       ifmsh->pre_value++;
+               pre_value = cpu_to_le16(ifmsh->pre_value);
+               memcpy(pos, &pre_value, 2);             /* Precedence Value */
+               pos += 2;
+               ifmsh->chsw_init = true;
+       }
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+ }
diff --combined net/wireless/ibss.c
index 403fe29c024db86f732b0c0d74b8d97d4cfc84bf,fa7461b6ba398fc56442ce75fafc6d5b5b0f8665..9d797df56649c5a47fdf1f61e665ee31e4b77e7c
@@@ -83,6 -83,8 +83,8 @@@ int __cfg80211_join_ibss(struct cfg8021
                         struct cfg80211_cached_keys *connkeys)
  {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_channel *check_chan;
+       u8 radar_detect_width = 0;
        int err;
  
        ASSERT_WDEV_LOCK(wdev);
        wdev->connect_keys = connkeys;
  
        wdev->ibss_fixed = params->channel_fixed;
+       wdev->ibss_dfs_possible = params->userspace_handles_dfs;
  #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
  #endif
+       check_chan = params->chandef.chan;
+       if (params->userspace_handles_dfs) {
+               /* use channel NULL to check for radar even if the current
+                * channel is not a radar channel - it might decide to change
+                * to DFS channel later.
+                */
+               radar_detect_width = BIT(params->chandef.width);
+               check_chan = NULL;
+       }
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          check_chan,
+                                          (params->channel_fixed &&
+                                           !radar_detect_width)
+                                          ? CHAN_MODE_SHARED
+                                          : CHAN_MODE_EXCLUSIVE,
+                                          radar_detect_width);
  
-       err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
-                                   params->channel_fixed
-                                   ? CHAN_MODE_SHARED
-                                   : CHAN_MODE_EXCLUSIVE);
        if (err) {
                wdev->connect_keys = NULL;
                return err;
@@@ -263,8 -279,6 +279,8 @@@ int cfg80211_ibss_wext_join(struct cfg8
                                if (chan->flags & IEEE80211_CHAN_DISABLED)
                                        continue;
                                wdev->wext.ibss.chandef.chan = chan;
 +                              wdev->wext.ibss.chandef.center_freq1 =
 +                                      chan->center_freq;
                                break;
                        }
  
@@@ -349,7 -363,6 +365,7 @@@ int cfg80211_ibss_wext_siwfreq(struct n
        if (chan) {
                wdev->wext.ibss.chandef.chan = chan;
                wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
 +              wdev->wext.ibss.chandef.center_freq1 = freq;
                wdev->wext.ibss.channel_fixed = true;
        } else {
                /* cfg80211_ibss_wext_join will pick one if needed */
diff --combined net/wireless/nl80211.c
index cbbef88a8ebd125fc4797e4493968e5c67108771,8ced6bc29f4a9c67d4c929110f16b455c8bac343..a7f4e7902104907adf86c9fffcfc0ab956d36095
@@@ -354,6 -354,9 +354,9 @@@ static const struct nla_policy nl80211_
        [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
        [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
        [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+       [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+       [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+       [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
  };
  
  /* policy for the key attributes */
@@@ -2421,7 -2424,7 +2424,7 @@@ static int nl80211_set_interface(struc
                change = true;
        }
  
 -      if (flags && (*flags & NL80211_MNTR_FLAG_ACTIVE) &&
 +      if (flags && (*flags & MONITOR_FLAG_ACTIVE) &&
            !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
                return -EOPNOTSUPP;
  
@@@ -2483,7 -2486,7 +2486,7 @@@ static int nl80211_new_interface(struc
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
  
 -      if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) &&
 +      if (!err && (flags & MONITOR_FLAG_ACTIVE) &&
            !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
                return -EOPNOTSUPP;
  
@@@ -3896,9 -3899,45 +3899,45 @@@ static int nl80211_parse_sta_wme(struc
        return 0;
  }
  
+ static int nl80211_parse_sta_channel_info(struct genl_info *info,
+                                     struct station_parameters *params)
+ {
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+               params->supported_channels =
+                    nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               params->supported_channels_len =
+                    nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               /*
+                * Need to include at least one (first channel, number of
+                * channels) tuple for each subband, and must have proper
+                * tuples for the rest of the data as well.
+                */
+               if (params->supported_channels_len < 2)
+                       return -EINVAL;
+               if (params->supported_channels_len % 2)
+                       return -EINVAL;
+       }
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+               params->supported_oper_classes =
+                nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               params->supported_oper_classes_len =
+                 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               /*
+                * The value of the Length field of the Supported Operating
+                * Classes element is between 2 and 253.
+                */
+               if (params->supported_oper_classes_len < 2 ||
+                   params->supported_oper_classes_len > 253)
+                       return -EINVAL;
+       }
+       return 0;
+ }
  static int nl80211_set_station_tdls(struct genl_info *info,
                                    struct station_parameters *params)
  {
+       int err;
        /* Dummy STA entry gets updated once the peer capabilities are known */
        if (info->attrs[NL80211_ATTR_PEER_AID])
                params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
                params->vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
  
+       err = nl80211_parse_sta_channel_info(info, params);
+       if (err)
+               return err;
        return nl80211_parse_sta_wme(info, params);
  }
  
@@@ -4089,6 -4132,10 +4132,10 @@@ static int nl80211_new_station(struct s
                        return -EINVAL;
        }
  
+       err = nl80211_parse_sta_channel_info(info, &params);
+       if (err)
+               return err;
        err = nl80211_parse_sta_wme(info, &params);
        if (err)
                return err;
@@@ -5653,6 -5700,7 +5700,7 @@@ static int nl80211_channel_switch(struc
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
                break;
        default:
                return -EOPNOTSUPP;
                return -EINVAL;
  
        /* only important for AP, IBSS and mesh create IEs internally */
-       if (need_new_beacon &&
-           (!info->attrs[NL80211_ATTR_CSA_IES] ||
-            !info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
+       if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
                return -EINVAL;
  
        params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
@@@ -5722,9 -5768,9 +5768,9 @@@ skip_beacons
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
  
-       /* DFS channels are only supported for AP/P2P GO ... for now. */
        if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
-           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
                err = cfg80211_chandef_dfs_required(wdev->wiphy,
                                                    &params.chandef);
                if (err < 0) {
@@@ -6556,6 -6602,9 +6602,9 @@@ static int nl80211_join_ibss(struct sk_
        ibss.control_port =
                nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
  
+       ibss.userspace_handles_dfs =
+               nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
        err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree(connkeys);
@@@ -10762,7 -10811,8 +10811,8 @@@ void cfg80211_ch_switch_notify(struct n
  
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
                    wdev->iftype != NL80211_IFTYPE_P2P_GO &&
-                   wdev->iftype != NL80211_IFTYPE_ADHOC))
+                   wdev->iftype != NL80211_IFTYPE_ADHOC &&
+                   wdev->iftype != NL80211_IFTYPE_MESH_POINT))
                goto out;
  
        wdev->channel = chandef->chan;
This page took 0.133786 seconds and 4 git commands to generate.