mac80211: set an external flag for TDLS stations
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / cfg.c
index 453e974287d19b52971116bca5e0e401746f3ffb..d8b236633ca333b48a1922a9c0a1b873edb8202c 100644 (file)
@@ -109,6 +109,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
 static int ieee80211_start_p2p_device(struct wiphy *wiphy,
                                      struct wireless_dev *wdev)
 {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int ret;
+
+       mutex_lock(&sdata->local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&sdata->local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
+
        return ieee80211_do_open(wdev, true);
 }
 
@@ -451,11 +460,11 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
        if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
-       if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
-       if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
-       if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+       if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
                rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
 }
 
@@ -970,15 +979,15 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        /* TODO: make hostapd tell us what it wants */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
        sdata->needed_rx_chains = sdata->local->rx_chains;
-       sdata->radar_required = params->radar_required;
 
        mutex_lock(&local->mtx);
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
+       if (!err)
+               ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
        mutex_unlock(&local->mtx);
        if (err)
                return err;
-       ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
 
        /*
         * Apply control port protocol, this allows us to
@@ -1056,6 +1065,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        int err;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       sdata_assert_lock(sdata);
 
        /* don't allow changing the beacon while CSA is in place - offset
         * of channel switch counter may change
@@ -1083,6 +1093,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        struct probe_resp *old_probe_resp;
        struct cfg80211_chan_def chandef;
 
+       sdata_assert_lock(sdata);
+
        old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
        if (!old_beacon)
                return -ENOENT;
@@ -1128,8 +1140,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
        skb_queue_purge(&sdata->u.ap.ps.bc_buf);
 
-       ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
        mutex_lock(&local->mtx);
+       ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
        ieee80211_vif_release_channel(sdata);
        mutex_unlock(&local->mtx);
 
@@ -1343,6 +1355,15 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                    params->vht_capa, sta);
 
+       if (params->opmode_notif_used) {
+               /* returned value is only needed for rc update, but the
+                * rc isn't initialized here yet, so ignore it
+                */
+               __ieee80211_vht_handle_opmode(sdata, sta,
+                                             params->opmode_notif,
+                                             band, false);
+       }
+
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 #ifdef CONFIG_MAC80211_MESH
                u32 changed = 0;
@@ -1438,6 +1459,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
                sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
                sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       } else {
+               sta->sta.tdls = true;
        }
 
        err = sta_apply_parameters(local, sta, params);
@@ -1554,7 +1577,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
                    sta->sdata->u.vlan.sta) {
-                       rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL);
+                       RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
                        prev_4addr = true;
                }
 
@@ -2630,6 +2653,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        if (!roc)
                return -ENOMEM;
 
+       /*
+        * If the duration is zero, then the driver
+        * wouldn't actually do anything. Set it to
+        * 10 for now.
+        *
+        * TODO: cancel the off-channel operation
+        *       when we get the SKB's TX status and
+        *       the wait time was zero before.
+        */
+       if (!duration)
+               duration = 10;
+
        roc->chan = channel;
        roc->duration = duration;
        roc->req_duration = duration;
@@ -2671,18 +2706,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 
        /* otherwise actually kick it off here (for error handling) */
 
-       /*
-        * If the duration is zero, then the driver
-        * wouldn't actually do anything. Set it to
-        * 10 for now.
-        *
-        * TODO: cancel the off-channel operation
-        *       when we get the SKB's TX status and
-        *       the wait time was zero before.
-        */
-       if (!duration)
-               duration = 10;
-
        ret = drv_remain_on_channel(local, sdata, channel, duration, type);
        if (ret) {
                kfree(roc);
@@ -2902,11 +2925,11 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
 
 static int ieee80211_start_radar_detection(struct wiphy *wiphy,
                                           struct net_device *dev,
-                                          struct cfg80211_chan_def *chandef)
+                                          struct cfg80211_chan_def *chandef,
+                                          u32 cac_time_ms)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
-       unsigned long timeout;
        int err;
 
        mutex_lock(&local->mtx);
@@ -2918,16 +2941,15 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
        /* whatever, but channel contexts should not complain about that one */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
        sdata->needed_rx_chains = local->rx_chains;
-       sdata->radar_required = true;
 
        err = ieee80211_vif_use_channel(sdata, chandef,
                                        IEEE80211_CHANCTX_SHARED);
        if (err)
                goto out_unlock;
 
-       timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
        ieee80211_queue_delayed_work(&sdata->local->hw,
-                                    &sdata->dfs_cac_timer_work, timeout);
+                                    &sdata->dfs_cac_timer_work,
+                                    msecs_to_jiffies(cac_time_ms));
 
  out_unlock:
        mutex_unlock(&local->mtx);
@@ -2990,136 +3012,135 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
        return new_beacon;
 }
 
-void ieee80211_csa_finalize_work(struct work_struct *work)
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       ieee80211_queue_work(&sdata->local->hw,
+                            &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
+
+static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_sub_if_data *sdata =
-               container_of(work, struct ieee80211_sub_if_data,
-                            csa_finalize_work);
        struct ieee80211_local *local = sdata->local;
        int err, changed = 0;
 
-       sdata_lock(sdata);
-       /* AP might have been stopped while waiting for the lock. */
-       if (!sdata->vif.csa_active)
-               goto unlock;
-
-       if (!ieee80211_sdata_running(sdata))
-               goto unlock;
+       sdata_assert_lock(sdata);
 
-       sdata->radar_required = sdata->csa_radar_required;
        mutex_lock(&local->mtx);
+       sdata->radar_required = sdata->csa_radar_required;
        err = ieee80211_vif_change_channel(sdata, &changed);
        mutex_unlock(&local->mtx);
        if (WARN_ON(err < 0))
-               goto unlock;
+               return;
 
        if (!local->use_chanctx) {
                local->_oper_chandef = sdata->csa_chandef;
                ieee80211_hw_config(local, 0);
        }
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
        sdata->vif.csa_active = false;
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
-               if (err < 0)
-                       goto unlock;
-
-               changed |= err;
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
 
-               ieee80211_bss_info_change_notify(sdata, err);
+               if (err < 0)
+                       return;
+               changed |= err;
                break;
        case NL80211_IFTYPE_ADHOC:
-               ieee80211_ibss_finish_csa(sdata);
+               err = ieee80211_ibss_finish_csa(sdata);
+               if (err < 0)
+                       return;
+               changed |= err;
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                err = ieee80211_mesh_finish_csa(sdata);
                if (err < 0)
-                       goto unlock;
+                       return;
+               changed |= err;
                break;
 #endif
        default:
                WARN_ON(1);
-               goto unlock;
+               return;
        }
 
+       ieee80211_bss_info_change_notify(sdata, changed);
+
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
        cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
-
-unlock:
-       sdata_unlock(sdata);
 }
 
-int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
-                            struct cfg80211_csa_settings *params)
+void ieee80211_csa_finalize_work(struct work_struct *work)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       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;
-
-       lockdep_assert_held(&sdata->wdev.mtx);
-
-       if (!list_empty(&local->roc_list) || local->scanning)
-               return -EBUSY;
-
-       if (sdata->wdev.cac_started)
-               return -EBUSY;
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            csa_finalize_work);
 
-       if (cfg80211_chandef_identical(&params->chandef,
-                                      &sdata->vif.bss_conf.chandef))
-               return -EINVAL;
+       sdata_lock(sdata);
+       /* AP might have been stopped while waiting for the lock. */
+       if (!sdata->vif.csa_active)
+               goto unlock;
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (!chanctx_conf) {
-               rcu_read_unlock();
-               return -EBUSY;
-       }
+       if (!ieee80211_sdata_running(sdata))
+               goto unlock;
 
-       /* don't handle for multi-VIF cases */
-       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1) {
-               rcu_read_unlock();
-               return -EBUSY;
-       }
-       num_chanctx = 0;
-       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
-               num_chanctx++;
-       rcu_read_unlock();
+       ieee80211_csa_finalize(sdata);
 
-       if (num_chanctx > 1)
-               return -EBUSY;
+unlock:
+       sdata_unlock(sdata);
+}
 
-       /* don't allow another channel switch if one is already active. */
-       if (sdata->vif.csa_active)
-               return -EBUSY;
+static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                                   struct cfg80211_csa_settings *params,
+                                   u32 *changed)
+{
+       int err;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
-               sdata->csa_counter_offset_beacon =
-                       params->counter_offset_beacon;
-               sdata->csa_counter_offset_presp = params->counter_offset_presp;
                sdata->u.ap.next_beacon =
                        cfg80211_beacon_dup(&params->beacon_after);
                if (!sdata->u.ap.next_beacon)
                        return -ENOMEM;
 
+               /*
+                * With a count of 0, we don't have to wait for any
+                * TBTT before switching, so complete the CSA
+                * immediately.  In theory, with a count == 1 we
+                * should delay the switch until just before the next
+                * TBTT, but that would complicate things so we switch
+                * immediately too.  If we would delay the switch
+                * until the next TBTT, we would have to set the probe
+                * response here.
+                *
+                * TODO: A channel switch with count <= 1 without
+                * sending a CSA action frame is kind of useless,
+                * because the clients won't know we're changing
+                * channels.  The action frame must be implemented
+                * either here or in the userspace.
+                */
+               if (params->count <= 1)
+                       break;
+
+               sdata->csa_counter_offset_beacon =
+                       params->counter_offset_beacon;
+               sdata->csa_counter_offset_presp = params->counter_offset_presp;
                err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
                        return err;
                }
+               *changed |= err;
+
                break;
        case NL80211_IFTYPE_ADHOC:
                if (!sdata->vif.bss_conf.ibss_joined)
@@ -3147,16 +3168,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                    params->chandef.chan->band)
                        return -EINVAL;
 
-               err = ieee80211_ibss_csa_beacon(sdata, params);
-               if (err < 0)
-                       return err;
+               /* see comments in the NL80211_IFTYPE_AP block */
+               if (params->count > 1) {
+                       err = ieee80211_ibss_csa_beacon(sdata, params);
+                       if (err < 0)
+                               return err;
+                       *changed |= err;
+               }
+
+               ieee80211_send_action_csa(sdata, params);
+
                break;
 #ifdef CONFIG_MAC80211_MESH
-       case NL80211_IFTYPE_MESH_POINT:
-               ifmsh = &sdata->u.mesh;
-
-               if (!ifmsh->mesh_id)
-                       return -EINVAL;
+       case NL80211_IFTYPE_MESH_POINT: {
+               struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
                if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
                        return -EINVAL;
@@ -3166,23 +3191,88 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                    params->chandef.chan->band)
                        return -EINVAL;
 
-               ifmsh->chsw_init = true;
-               if (!ifmsh->pre_value)
-                       ifmsh->pre_value = 1;
-               else
-                       ifmsh->pre_value++;
+               if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) {
+                       ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
+                       if (!ifmsh->pre_value)
+                               ifmsh->pre_value = 1;
+                       else
+                               ifmsh->pre_value++;
+               }
 
-               err = ieee80211_mesh_csa_beacon(sdata, params, true);
-               if (err < 0) {
-                       ifmsh->chsw_init = false;
-                       return err;
+               /* see comments in the NL80211_IFTYPE_AP block */
+               if (params->count > 1) {
+                       err = ieee80211_mesh_csa_beacon(sdata, params);
+                       if (err < 0) {
+                               ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
+                               return err;
+                       }
+                       *changed |= err;
                }
+
+               if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
+                       ieee80211_send_action_csa(sdata, params);
+
                break;
+               }
 #endif
        default:
                return -EOPNOTSUPP;
        }
 
+       return 0;
+}
+
+int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                            struct cfg80211_csa_settings *params)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *chanctx;
+       int err, num_chanctx, changed = 0;
+
+       sdata_assert_lock(sdata);
+
+       if (!list_empty(&local->roc_list) || local->scanning)
+               return -EBUSY;
+
+       if (sdata->wdev.cac_started)
+               return -EBUSY;
+
+       if (cfg80211_chandef_identical(&params->chandef,
+                                      &sdata->vif.bss_conf.chandef))
+               return -EINVAL;
+
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               mutex_unlock(&local->chanctx_mtx);
+               return -EBUSY;
+       }
+
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(conf, struct ieee80211_chanctx, conf);
+       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
+               mutex_unlock(&local->chanctx_mtx);
+               return -EBUSY;
+       }
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
+               num_chanctx++;
+       mutex_unlock(&local->chanctx_mtx);
+
+       if (num_chanctx > 1)
+               return -EBUSY;
+
+       /* don't allow another channel switch if one is already active. */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+
+       err = ieee80211_set_csa_beacon(sdata, params, &changed);
+       if (err)
+               return err;
+
        sdata->csa_radar_required = params->radar_required;
 
        if (params->block_tx)
@@ -3193,8 +3283,13 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        sdata->csa_chandef = params->chandef;
        sdata->vif.csa_active = true;
 
-       ieee80211_bss_info_change_notify(sdata, err);
-       drv_channel_switch_beacon(sdata, &params->chandef);
+       if (changed) {
+               ieee80211_bss_info_change_notify(sdata, changed);
+               drv_channel_switch_beacon(sdata, &params->chandef);
+       } else {
+               /* if the beacon didn't change, we can finalize immediately */
+               ieee80211_csa_finalize(sdata);
+       }
 
        return 0;
 }
@@ -3573,8 +3668,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
 
 static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                               u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, const u8 *extra_ies,
-                              size_t extra_ies_len)
+                              u16 status_code, u32 peer_capability,
+                              const u8 *extra_ies, size_t extra_ies_len)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
@@ -3865,7 +3960,22 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
        return 0;
 }
 
-struct cfg80211_ops mac80211_config_ops = {
+static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
+                                     struct net_device *dev,
+                                     struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int ret;
+       u32 changed = 0;
+
+       ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed);
+       if (ret == 0)
+               ieee80211_bss_info_change_notify(sdata, changed);
+
+       return ret;
+}
+
+const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
        .change_virtual_intf = ieee80211_change_iface,
@@ -3945,4 +4055,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .start_radar_detection = ieee80211_start_radar_detection,
        .channel_switch = ieee80211_channel_switch,
        .set_qos_map = ieee80211_set_qos_map,
+       .set_ap_chanwidth = ieee80211_set_ap_chanwidth,
 };