summaryrefslogtreecommitdiff
path: root/net/mac80211/cfg.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r--net/mac80211/cfg.c186
1 files changed, 147 insertions, 39 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 2ed07fa121ab..d9aca1c3c097 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -311,6 +311,96 @@ static void ieee80211_stop_p2p_device(struct wiphy *wiphy,
ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev));
}
+static void ieee80211_nan_conf_free(struct cfg80211_nan_conf *conf)
+{
+ kfree(conf->cluster_id);
+ kfree(conf->extra_nan_attrs);
+ kfree(conf->vendor_elems);
+ memset(conf, 0, sizeof(*conf));
+}
+
+static void ieee80211_stop_nan(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+ if (!sdata->u.nan.started)
+ return;
+
+ drv_stop_nan(sdata->local, sdata);
+ sdata->u.nan.started = false;
+
+ ieee80211_nan_conf_free(&sdata->u.nan.conf);
+
+ ieee80211_sdata_stop(sdata);
+ ieee80211_recalc_idle(sdata->local);
+}
+
+static int ieee80211_nan_conf_copy(struct cfg80211_nan_conf *dst,
+ struct cfg80211_nan_conf *src,
+ u32 changes)
+{
+ if (changes & CFG80211_NAN_CONF_CHANGED_PREF)
+ dst->master_pref = src->master_pref;
+
+ if (changes & CFG80211_NAN_CONF_CHANGED_BANDS)
+ dst->bands = src->bands;
+
+ if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG) {
+ dst->scan_period = src->scan_period;
+ dst->scan_dwell_time = src->scan_dwell_time;
+ dst->discovery_beacon_interval =
+ src->discovery_beacon_interval;
+ dst->enable_dw_notification = src->enable_dw_notification;
+ memcpy(&dst->band_cfgs, &src->band_cfgs,
+ sizeof(dst->band_cfgs));
+
+ kfree(dst->cluster_id);
+ dst->cluster_id = NULL;
+
+ kfree(dst->extra_nan_attrs);
+ dst->extra_nan_attrs = NULL;
+ dst->extra_nan_attrs_len = 0;
+
+ kfree(dst->vendor_elems);
+ dst->vendor_elems = NULL;
+ dst->vendor_elems_len = 0;
+
+ if (src->cluster_id) {
+ dst->cluster_id = kmemdup(src->cluster_id, ETH_ALEN,
+ GFP_KERNEL);
+ if (!dst->cluster_id)
+ goto no_mem;
+ }
+
+ if (src->extra_nan_attrs && src->extra_nan_attrs_len) {
+ dst->extra_nan_attrs = kmemdup(src->extra_nan_attrs,
+ src->extra_nan_attrs_len,
+ GFP_KERNEL);
+ if (!dst->extra_nan_attrs)
+ goto no_mem;
+
+ dst->extra_nan_attrs_len = src->extra_nan_attrs_len;
+ }
+
+ if (src->vendor_elems && src->vendor_elems_len) {
+ dst->vendor_elems = kmemdup(src->vendor_elems,
+ src->vendor_elems_len,
+ GFP_KERNEL);
+ if (!dst->vendor_elems)
+ goto no_mem;
+
+ dst->vendor_elems_len = src->vendor_elems_len;
+ }
+ }
+
+ return 0;
+
+no_mem:
+ ieee80211_nan_conf_free(dst);
+ return -ENOMEM;
+}
+
static int ieee80211_start_nan(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_nan_conf *conf)
@@ -320,6 +410,9 @@ static int ieee80211_start_nan(struct wiphy *wiphy,
lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ if (sdata->u.nan.started)
+ return -EALREADY;
+
ret = ieee80211_check_combinations(sdata, NULL, 0, 0, -1);
if (ret < 0)
return ret;
@@ -329,21 +422,21 @@ static int ieee80211_start_nan(struct wiphy *wiphy,
return ret;
ret = drv_start_nan(sdata->local, sdata, conf);
- if (ret)
+ if (ret) {
ieee80211_sdata_stop(sdata);
+ return ret;
+ }
- sdata->u.nan.conf = *conf;
-
- return ret;
-}
+ sdata->u.nan.started = true;
+ ieee80211_recalc_idle(sdata->local);
-static void ieee80211_stop_nan(struct wiphy *wiphy,
- struct wireless_dev *wdev)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ ret = ieee80211_nan_conf_copy(&sdata->u.nan.conf, conf, 0xFFFFFFFF);
+ if (ret) {
+ ieee80211_stop_nan(wiphy, wdev);
+ return ret;
+ }
- drv_stop_nan(sdata->local, sdata);
- ieee80211_sdata_stop(sdata);
+ return 0;
}
static int ieee80211_nan_change_conf(struct wiphy *wiphy,
@@ -352,7 +445,7 @@ static int ieee80211_nan_change_conf(struct wiphy *wiphy,
u32 changes)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct cfg80211_nan_conf new_conf;
+ struct cfg80211_nan_conf new_conf = {};
int ret = 0;
if (sdata->vif.type != NL80211_IFTYPE_NAN)
@@ -361,17 +454,28 @@ static int ieee80211_nan_change_conf(struct wiphy *wiphy,
if (!ieee80211_sdata_running(sdata))
return -ENETDOWN;
- new_conf = sdata->u.nan.conf;
+ if (!changes)
+ return 0;
- if (changes & CFG80211_NAN_CONF_CHANGED_PREF)
- new_conf.master_pref = conf->master_pref;
+ /* First make a full copy of the previous configuration and then apply
+ * the changes. This might be a little wasteful, but it is simpler.
+ */
+ ret = ieee80211_nan_conf_copy(&new_conf, &sdata->u.nan.conf,
+ 0xFFFFFFFF);
+ if (ret < 0)
+ return ret;
- if (changes & CFG80211_NAN_CONF_CHANGED_BANDS)
- new_conf.bands = conf->bands;
+ ret = ieee80211_nan_conf_copy(&new_conf, conf, changes);
+ if (ret < 0)
+ return ret;
ret = drv_nan_change_conf(sdata->local, sdata, &new_conf, changes);
- if (!ret)
+ if (ret) {
+ ieee80211_nan_conf_free(&new_conf);
+ } else {
+ ieee80211_nan_conf_free(&sdata->u.nan.conf);
sdata->u.nan.conf = new_conf;
+ }
return ret;
}
@@ -2171,10 +2275,16 @@ static int sta_apply_parameters(struct ieee80211_local *local,
/*
* cfg80211 validates this (1-2007) and allows setting the AID
- * only when creating a new station entry
+ * only when creating a new station entry. For S1G APs, the current
+ * implementation supports a maximum of 1600 AIDs.
*/
- if (params->aid)
+ if (params->aid) {
+ if (sdata->vif.cfg.s1g &&
+ params->aid > IEEE80211_MAX_SUPPORTED_S1G_AID)
+ return -EINVAL;
+
sta->sta.aid = params->aid;
+ }
/*
* Some of the following updates would be racy if called on an
@@ -3001,6 +3111,9 @@ static int ieee80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *req)
{
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_link_data *link;
+ struct ieee80211_channel *chan;
+ int radio_idx;
sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev);
@@ -3028,10 +3141,20 @@ static int ieee80211_scan(struct wiphy *wiphy,
* the frames sent while scanning on other channel will be
* lost)
*/
- if (ieee80211_num_beaconing_links(sdata) &&
- (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
- !(req->flags & NL80211_SCAN_FLAG_AP)))
- return -EOPNOTSUPP;
+ for_each_link_data(sdata, link) {
+ /* if the link is not beaconing, ignore it */
+ if (!sdata_dereference(link->u.ap.beacon, sdata))
+ continue;
+
+ chan = link->conf->chanreq.oper.chan;
+ radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan);
+
+ if (ieee80211_is_radio_idx_in_scan_req(wiphy, req,
+ radio_idx) &&
+ (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
+ !(req->flags & NL80211_SCAN_FLAG_AP)))
+ return -EOPNOTSUPP;
+ }
break;
case NL80211_IFTYPE_NAN:
default:
@@ -3677,12 +3800,7 @@ static bool ieee80211_is_scan_ongoing(struct wiphy *wiphy,
if (list_empty(&local->roc_list) && !local->scanning)
return false;
- if (wiphy->n_radio < 2)
- return true;
-
req_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chandef->chan);
- if (req_radio_idx < 0)
- return true;
if (local->scanning) {
scan_req = wiphy_dereference(wiphy, local->scan_req);
@@ -3701,14 +3819,6 @@ static bool ieee80211_is_scan_ongoing(struct wiphy *wiphy,
list_for_each_entry(roc, &local->roc_list, list) {
chan_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy,
roc->chan);
- /*
- * The roc work is added but chan_radio_idx is invalid.
- * Should not happen but if it does, let's not take
- * risk and return true.
- */
- if (chan_radio_idx < 0)
- return true;
-
if (chan_radio_idx == req_radio_idx)
return true;
}
@@ -4825,7 +4935,6 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
int ret = 0;
spin_lock_bh(&local->fq.lock);
- rcu_read_lock();
if (wdev) {
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -4851,7 +4960,6 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
}
out:
- rcu_read_unlock();
spin_unlock_bh(&local->fq.lock);
return ret;