summaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2013-12-02 14:25:38 -0500
committerJohn W. Linville <linville@tuxdriver.com>2013-12-02 14:25:38 -0500
commit4b074b07625f603d40d4d04937f8874a00415dc4 (patch)
tree2dffdc46e3fea0320524f483cf5ac2c058ab59f2 /net/wireless
parent7d68849f40cd9169088249cc889d95c8998c3fb8 (diff)
parentddcc347b70f298f9d624cd0e250581d831d915fb (diff)
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/chan.c203
-rw-r--r--net/wireless/core.c6
-rw-r--r--net/wireless/core.h18
-rw-r--r--net/wireless/genregdb.awk45
-rw-r--r--net/wireless/ibss.c4
-rw-r--r--net/wireless/mesh.c3
-rw-r--r--net/wireless/mlme.c20
-rw-r--r--net/wireless/nl80211.c107
-rw-r--r--net/wireless/nl80211.h2
-rw-r--r--net/wireless/rdev-ops.h12
-rw-r--r--net/wireless/reg.c802
-rw-r--r--net/wireless/reg.h3
-rw-r--r--net/wireless/trace.h15
13 files changed, 787 insertions, 453 deletions
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 9b8cc877eb1..78559b5bbd1 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -277,6 +277,32 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
width, dfs_state);
}
+static u32 cfg80211_get_start_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 start_freq;
+
+ if (bandwidth <= 20)
+ start_freq = center_freq;
+ else
+ start_freq = center_freq - bandwidth/2 + 10;
+
+ return start_freq;
+}
+
+static u32 cfg80211_get_end_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 end_freq;
+
+ if (bandwidth <= 20)
+ end_freq = center_freq;
+ else
+ end_freq = center_freq + bandwidth/2 - 10;
+
+ return end_freq;
+}
+
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
u32 center_freq,
u32 bandwidth)
@@ -284,13 +310,8 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
struct ieee80211_channel *c;
u32 freq, start_freq, end_freq;
- if (bandwidth <= 20) {
- start_freq = center_freq;
- end_freq = center_freq;
- } else {
- start_freq = center_freq - bandwidth/2 + 10;
- end_freq = center_freq + bandwidth/2 - 10;
- }
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
@@ -330,33 +351,159 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
-static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
- u32 center_freq, u32 bandwidth,
- u32 prohibited_flags)
+static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
{
struct ieee80211_channel *c;
u32 freq, start_freq, end_freq;
+ int count = 0;
- if (bandwidth <= 20) {
- start_freq = center_freq;
- end_freq = center_freq;
- } else {
- start_freq = center_freq - bandwidth/2 + 10;
- end_freq = center_freq + bandwidth/2 - 10;
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * Check all channels are DFS channels (DFS_USABLE or
+ * DFS_AVAILABLE). Return number of usable channels
+ * (require CAC). Allow DFS and non-DFS channel mix.
+ */
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_RADAR) {
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
+ return -EINVAL;
+
+ if (c->dfs_state == NL80211_DFS_USABLE)
+ count++;
+ }
}
+ return count;
+}
+
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r1, r2 = 0;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1,
+ width);
+
+ if (r1 < 0)
+ return false;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r2 = cfg80211_get_chans_dfs_usable(wiphy,
+ chandef->center_freq2,
+ width);
+ if (r2 < 0)
+ return false;
+ break;
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return (r1 + r2 > 0);
+}
+
+
+static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * If any channel in between is disabled or has not
+ * had gone through CAC return false
+ */
for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c)
return false;
- /* check for radar flags */
- if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return false;
+
+ if ((c->flags & IEEE80211_CHAN_RADAR) &&
(c->dfs_state != NL80211_DFS_AVAILABLE))
return false;
+ }
+
+ return true;
+}
+
+static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1,
+ width);
+
+ /* If any of channels unavailable for cf1 just return */
+ if (!r)
+ return r;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r = cfg80211_get_chans_dfs_available(wiphy,
+ chandef->center_freq2,
+ width);
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return r;
+}
- /* check for the other flags */
- if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
+
+static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || c->flags & prohibited_flags)
return false;
}
@@ -462,14 +609,19 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
bool res;
+ u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR;
trace_cfg80211_reg_can_beacon(wiphy, chandef);
- res = cfg80211_chandef_usable(wiphy, chandef,
- IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_PASSIVE_SCAN |
- IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_RADAR);
+ if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 &&
+ cfg80211_chandef_dfs_available(wiphy, chandef)) {
+ /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
+ prohibited_flags = IEEE80211_CHAN_DISABLED;
+ }
+
+ res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
trace_cfg80211_return_bool(res);
return res;
@@ -510,6 +662,7 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
: CHAN_MODE_EXCLUSIVE;
return;
}
+ break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (wdev->current_bss) {
diff --git a/net/wireless/core.c b/net/wireless/core.c
index aff959e5a1b..fc968c861ee 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -357,8 +357,6 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy.rts_threshold = (u32) -1;
rdev->wiphy.coverage_class = 0;
- rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
-
return &rdev->wiphy;
}
EXPORT_SYMBOL(wiphy_new);
@@ -566,6 +564,8 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
+ rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH;
+
rtnl_lock();
res = device_add(&rdev->wiphy.dev);
if (res) {
@@ -586,7 +586,7 @@ int wiphy_register(struct wiphy *wiphy)
if (IS_ERR(rdev->wiphy.debugfsdir))
rdev->wiphy.debugfsdir = NULL;
- if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
+ if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
struct regulatory_request request;
request.wiphy_idx = get_wiphy_idx(wiphy);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index af10e59af2d..0a277c33bb0 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -317,9 +317,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, const u8 *buf, size_t len,
- bool no_cck, bool dont_wait_for_ack, u64 *cookie);
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie);
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask);
void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
@@ -382,6 +381,19 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
enum cfg80211_chan_mode chanmode,
u8 radar_detect);
+/**
+ * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ *
+ * Checks if chandef is usable and we can/need start CAC on such channel.
+ *
+ * Return: Return true if all channels available and at least
+ * one channel require CAC (NL80211_DFS_USABLE)
+ */
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef);
+
void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state);
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
index 42ed274e81f..9a8217d2a90 100644
--- a/net/wireless/genregdb.awk
+++ b/net/wireless/genregdb.awk
@@ -33,15 +33,7 @@ BEGIN {
regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
}
-/^[ \t]*#/ {
- # Ignore
-}
-
-!active && /^[ \t]*$/ {
- # Ignore
-}
-
-!active && /country/ {
+function parse_country_head() {
country=$2
sub(/:/, "", country)
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
@@ -57,7 +49,8 @@ BEGIN {
regdb = regdb "\t&regdom_" country ",\n"
}
-active && /^[ \t]*\(/ {
+function parse_reg_rule()
+{
start = $1
sub(/\(/, "", start)
end = $3
@@ -107,17 +100,21 @@ active && /^[ \t]*\(/ {
} else if (flagarray[arg] == "PTMP-ONLY") {
flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
} else if (flagarray[arg] == "PASSIVE-SCAN") {
- flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
} else if (flagarray[arg] == "NO-IBSS") {
- flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+ } else if (flagarray[arg] == "NO-IR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
}
+
}
flags = flags "0"
printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
rules++
}
-active && /^[ \t]*$/ {
+function print_tail_country()
+{
active = 0
printf "\t},\n"
printf "\t.n_reg_rules = %d\n", rules
@@ -125,7 +122,29 @@ active && /^[ \t]*$/ {
rules = 0;
}
+/^[ \t]*#/ {
+ # Ignore
+}
+
+!active && /^[ \t]*$/ {
+ # Ignore
+}
+
+!active && /country/ {
+ parse_country_head()
+}
+
+active && /^[ \t]*\(/ {
+ parse_reg_rule()
+}
+
+active && /^[ \t]*$/ {
+ print_tail_country()
+}
+
END {
+ if (active)
+ print_tail_country()
print regdb "};"
print ""
print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 9d797df5664..f7910571294 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -274,7 +274,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
- if (chan->flags & IEEE80211_CHAN_NO_IBSS)
+ if (chan->flags & IEEE80211_CHAN_NO_IR)
continue;
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
@@ -345,7 +345,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
chan = ieee80211_get_channel(wdev->wiphy, freq);
if (!chan)
return -EINVAL;
- if (chan->flags & IEEE80211_CHAN_NO_IBSS ||
+ if (chan->flags & IEEE80211_CHAN_NO_IR ||
chan->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
}
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 0553fd4d85a..b0e1869de7d 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -141,8 +141,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
- if (chan->flags & (IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN |
+ if (chan->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_RADAR))
continue;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 6a6b1c8e907..52cca05044a 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -520,9 +520,7 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, const u8 *buf, size_t len,
- bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
{
const struct ieee80211_mgmt *mgmt;
u16 stype;
@@ -533,10 +531,10 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
if (!rdev->ops->mgmt_tx)
return -EOPNOTSUPP;
- if (len < 24 + 1)
+ if (params->len < 24 + 1)
return -EINVAL;
- mgmt = (const struct ieee80211_mgmt *) buf;
+ mgmt = (const struct ieee80211_mgmt *)params->buf;
if (!ieee80211_is_mgmt(mgmt->frame_control))
return -EINVAL;
@@ -615,9 +613,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
return -EINVAL;
/* Transmit the Action frame as requested by user space */
- return rdev_mgmt_tx(rdev, wdev, chan, offchan,
- wait, buf, len, no_cck, dont_wait_for_ack,
- cookie);
+ return rdev_mgmt_tx(rdev, wdev, params, cookie);
}
bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
@@ -763,12 +759,12 @@ void cfg80211_radar_event(struct wiphy *wiphy,
EXPORT_SYMBOL(cfg80211_radar_event);
void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event, gfp_t gfp)
{
struct wireless_dev *wdev = netdev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- struct cfg80211_chan_def chandef;
unsigned long timeout;
trace_cfg80211_cac_event(netdev, event);
@@ -779,14 +775,12 @@ void cfg80211_cac_event(struct net_device *netdev,
if (WARN_ON(!wdev->channel))
return;
- cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
-
switch (event) {
case NL80211_RADAR_CAC_FINISHED:
timeout = wdev->cac_start_time +
msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
WARN_ON(!time_after_eq(jiffies, timeout));
- cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
break;
case NL80211_RADAR_CAC_ABORTED:
break;
@@ -796,6 +790,6 @@ void cfg80211_cac_event(struct net_device *netdev,
}
wdev->cac_started = false;
- nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
+ nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
}
EXPORT_SYMBOL(cfg80211_cac_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a1eb2107317..efaa23e562b 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -564,12 +564,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
- goto nla_put_failure;
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
+ goto nla_put_failure;
+ if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
+ goto nla_put_failure;
+ }
if (chan->flags & IEEE80211_CHAN_RADAR) {
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
goto nla_put_failure;
@@ -1247,10 +1247,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
- nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
- goto nla_put_failure;
-
state->split_start++;
if (state->split)
break;
@@ -1579,6 +1575,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if (nl80211_send_coalesce(msg, dev))
goto nla_put_failure;
+ if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+ (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
+ goto nla_put_failure;
+
/* done */
state->split_start = 0;
break;
@@ -2187,7 +2188,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
}
static int nl80211_send_chandef(struct sk_buff *msg,
- struct cfg80211_chan_def *chandef)
+ const struct cfg80211_chan_def *chandef)
{
WARN_ON(!cfg80211_chandef_valid(chandef));
@@ -3236,6 +3237,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(params.acl);
}
+ wdev_lock(wdev);
err = rdev_start_ap(rdev, dev, &params);
if (!err) {
wdev->preset_chandef = params.chandef;
@@ -3244,6 +3246,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
}
+ wdev_unlock(wdev);
kfree(params.acl);
@@ -3272,7 +3275,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
- return rdev_change_beacon(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_change_beacon(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
@@ -4478,7 +4485,9 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
struct bss_parameters params;
+ int err;
memset(&params, 0, sizeof(params));
/* default to not changing parameters */
@@ -4544,7 +4553,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
- return rdev_change_bss(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_change_bss(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
@@ -5098,7 +5111,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
char *alpha2 = NULL;
int rem_reg_rules = 0, r = 0;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
- u8 dfs_region = 0;
+ enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
struct ieee80211_regdomain *rd = NULL;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -5119,6 +5132,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
+ if (!reg_is_valid_request(alpha2))
+ return -EINVAL;
+
size_of_regd = sizeof(struct ieee80211_regdomain) +
num_rules * sizeof(struct ieee80211_reg_rule);
@@ -5361,10 +5377,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
- if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
- !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
- ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
- !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+ if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
err = -EOPNOTSUPP;
goto out_free;
}
@@ -5604,10 +5618,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
- if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
- !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
- ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
- !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+ if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
err = -EOPNOTSUPP;
goto out_free;
}
@@ -5670,7 +5682,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (err == 0)
return -EINVAL;
- if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
+ if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))
return -EINVAL;
if (!rdev->ops->start_radar_detection)
@@ -5810,7 +5822,11 @@ skip_beacons:
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
params.block_tx = true;
- return rdev_channel_switch(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_channel_switch(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
@@ -7443,10 +7459,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
void *hdr = NULL;
u64 cookie;
struct sk_buff *msg = NULL;
- unsigned int wait = 0;
- bool offchan, no_cck, dont_wait_for_ack;
-
- dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
+ struct cfg80211_mgmt_tx_params params = {
+ .dont_wait_for_ack =
+ info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK],
+ };
if (!info->attrs[NL80211_ATTR_FRAME])
return -EINVAL;
@@ -7473,24 +7489,24 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_DURATION]) {
if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
return -EINVAL;
- wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+ params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
/*
* We should wait on the channel for at least a minimum amount
* of time (10ms) but no longer than the driver supports.
*/
- if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
- wait > rdev->wiphy.max_remain_on_channel_duration)
+ if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
+ params.wait > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
}
- offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
+ params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
- if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
+ if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
return -EINVAL;
- no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+ params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
/* get the channel if any has been specified, otherwise pass NULL to
* the driver. The latter will use the current one
@@ -7502,10 +7518,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
return err;
}
- if (!chandef.chan && offchan)
+ if (!chandef.chan && params.offchan)
return -EINVAL;
- if (!dont_wait_for_ack) {
+ if (!params.dont_wait_for_ack) {
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
@@ -7518,10 +7534,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
}
- err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
- nla_data(info->attrs[NL80211_ATTR_FRAME]),
- nla_len(info->attrs[NL80211_ATTR_FRAME]),
- no_cck, dont_wait_for_ack, &cookie);
+ params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+ params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+ params.chan = chandef.chan;
+ err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
if (err)
goto free_msg;
@@ -10805,21 +10821,18 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- trace_cfg80211_ch_switch_notify(dev, chandef);
+ ASSERT_WDEV_LOCK(wdev);
- wdev_lock(wdev);
+ trace_cfg80211_ch_switch_notify(dev, chandef);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
wdev->iftype != NL80211_IFTYPE_ADHOC &&
wdev->iftype != NL80211_IFTYPE_MESH_POINT))
- goto out;
+ return;
wdev->channel = chandef->chan;
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
-out:
- wdev_unlock(wdev);
- return;
}
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
@@ -10878,7 +10891,7 @@ EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
- struct cfg80211_chan_def *chandef,
+ const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp)
{
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 2c0f2b3c07c..b1b231324e1 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -70,7 +70,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
- struct cfg80211_chan_def *chandef,
+ const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 37ce9fdfe93..a6c03ab14a0 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -624,16 +624,12 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,
static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, const u8 *buf, size_t len,
- bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
{
int ret;
- trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
- wait, no_cck, dont_wait_for_ack);
- ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
- wait, buf, len, no_cck,
- dont_wait_for_ack, cookie);
+ trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params);
+ ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie);
trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
return ret;
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 7da67fd0b41..ec54e1aac8e 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -120,6 +120,21 @@ static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
return rtnl_dereference(wiphy->regd);
}
+static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region)
+{
+ switch (dfs_region) {
+ case NL80211_DFS_UNSET:
+ return "unset";
+ case NL80211_DFS_FCC:
+ return "FCC";
+ case NL80211_DFS_ETSI:
+ return "ETSI";
+ case NL80211_DFS_JP:
+ return "JP";
+ }
+ return "Unknown";
+}
+
static void rcu_free_regdom(const struct ieee80211_regdomain *r)
{
if (!r)
@@ -163,35 +178,29 @@ static const struct ieee80211_regdomain world_regdom = {
REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
/* IEEE 802.11b/g, channels 12..13. */
REG_RULE(2467-10, 2472+10, 40, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11 channel 14 - Only JP enables
* this and for 802.11b only */
REG_RULE(2484-10, 2484+10, 20, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_NO_OFDM),
/* IEEE 802.11a, channel 36..48 */
REG_RULE(5180-10, 5240+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11a, channel 52..64 - DFS required */
REG_RULE(5260-10, 5320+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 100..144 - DFS required */
REG_RULE(5500-10, 5720+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 149..165 */
REG_RULE(5745-10, 5825+10, 80, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11ad (60gHz), channels 1..3 */
REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
@@ -208,11 +217,26 @@ static char user_alpha2[2];
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
+static void reg_kfree_last_request(void)
+{
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+
+ if (lr != &core_request_world && lr)
+ kfree_rcu(lr, rcu_head);
+}
+
+static void reg_update_last_request(struct regulatory_request *request)
+{
+ reg_kfree_last_request();
+ rcu_assign_pointer(last_request, request);
+}
+
static void reset_regdomains(bool full_reset,
const struct ieee80211_regdomain *new_regdom)
{
const struct ieee80211_regdomain *r;
- struct regulatory_request *lr;
ASSERT_RTNL();
@@ -235,10 +259,7 @@ static void reset_regdomains(bool full_reset,
if (!full_reset)
return;
- lr = get_last_request();
- if (lr != &core_request_world && lr)
- kfree_rcu(lr, rcu_head);
- rcu_assign_pointer(last_request, &core_request_world);
+ reg_update_last_request(&core_request_world);
}
/*
@@ -456,7 +477,15 @@ static int call_crda(const char *alpha2)
return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
}
-static bool reg_is_valid_request(const char *alpha2)
+static enum reg_request_treatment
+reg_call_crda(struct regulatory_request *request)
+{
+ if (call_crda(request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_OK;
+}
+
+bool reg_is_valid_request(const char *alpha2)
{
struct regulatory_request *lr = get_last_request();
@@ -557,6 +586,20 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
}
/*
+ * Later on we can perhaps use the more restrictive DFS
+ * region but we don't have information for that yet so
+ * for now simply disallow conflicts.
+ */
+static enum nl80211_dfs_regions
+reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
+ const enum nl80211_dfs_regions dfs_region2)
+{
+ if (dfs_region1 != dfs_region2)
+ return NL80211_DFS_UNSET;
+ return dfs_region1;
+}
+
+/*
* Helper for regdom_intersect(), this does the real
* mathematical intersection fun
*/
@@ -687,6 +730,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
rd->n_reg_rules = num_rules;
rd->alpha2[0] = '9';
rd->alpha2[1] = '8';
+ rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region,
+ rd2->dfs_region);
return rd;
}
@@ -698,10 +743,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
static u32 map_regdom_flags(u32 rd_flags)
{
u32 channel_flags = 0;
- if (rd_flags & NL80211_RRF_PASSIVE_SCAN)
- channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN;
- if (rd_flags & NL80211_RRF_NO_IBSS)
- channel_flags |= IEEE80211_CHAN_NO_IBSS;
+ if (rd_flags & NL80211_RRF_NO_IR_ALL)
+ channel_flags |= IEEE80211_CHAN_NO_IR;
if (rd_flags & NL80211_RRF_DFS)
channel_flags |= IEEE80211_CHAN_RADAR;
if (rd_flags & NL80211_RRF_NO_OFDM)
@@ -854,8 +897,18 @@ static void handle_channel(struct wiphy *wiphy,
PTR_ERR(reg_rule) == -ERANGE)
return;
- REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq);
- chan->flags |= IEEE80211_CHAN_DISABLED;
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ request_wiphy && request_wiphy == wiphy &&
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
+ REG_DBG_PRINT("Disabling freq %d MHz for good\n",
+ chan->center_freq);
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
+ } else {
+ REG_DBG_PRINT("Disabling freq %d MHz\n",
+ chan->center_freq);
+ chan->flags |= IEEE80211_CHAN_DISABLED;
+ }
return;
}
@@ -873,7 +926,7 @@ static void handle_channel(struct wiphy *wiphy,
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
- request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
/*
* This guarantees the driver's requested regulatory domain
* will always be used as a base for further regulatory
@@ -899,13 +952,11 @@ static void handle_channel(struct wiphy *wiphy,
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
if (chan->orig_mpwr) {
/*
- * Devices that have their own custom regulatory domain
- * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
- * passed country IE power settings.
+ * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
+ * will always follow the passed country IE power settings.
*/
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER)
chan->max_power = chan->max_reg_power;
else
chan->max_power = min(chan->orig_mpwr,
@@ -975,8 +1026,8 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
{
- if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
- !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
+ if (wiphy->regulatory_flags & REGULATORY_STRICT_REG &&
+ !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG))
return true;
return false;
}
@@ -994,7 +1045,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
}
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
REG_DBG_PRINT("Ignoring regulatory request set by %s "
"since the driver uses its own custom "
"regulatory domain\n",
@@ -1032,7 +1083,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy)
return true;
if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
return true;
return false;
@@ -1060,19 +1111,14 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
if (!reg_is_world_roaming(wiphy))
return;
- if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS)
+ if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS)
return;
chan_before.center_freq = chan->center_freq;
chan_before.flags = chan->flags;
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
- chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
- channel_changed = true;
- }
-
- if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
- chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ chan->flags &= ~IEEE80211_CHAN_NO_IR;
channel_changed = true;
}
@@ -1205,14 +1251,30 @@ static void reg_process_ht_flags(struct wiphy *wiphy)
reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
}
+static void reg_call_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ if (wiphy->reg_notifier)
+ wiphy->reg_notifier(wiphy, request);
+}
+
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
enum ieee80211_band band;
struct regulatory_request *lr = get_last_request();
- if (ignore_reg_update(wiphy, initiator))
+ if (ignore_reg_update(wiphy, initiator)) {
+ /*
+ * Regulatory updates set by CORE are ignored for custom
+ * regulatory cards. Let us notify the changes to the driver,
+ * as some drivers used this to restore its orig_* reg domain.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_CORE &&
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
+ reg_call_notifier(wiphy, lr);
return;
+ }
lr->dfs_region = get_cfg80211_regdom()->dfs_region;
@@ -1221,9 +1283,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
reg_process_beacons(wiphy);
reg_process_ht_flags(wiphy);
-
- if (wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, lr);
+ reg_call_notifier(wiphy, lr);
}
static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
@@ -1236,15 +1296,6 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator);
- /*
- * Regulatory updates set by CORE are ignored for custom
- * regulatory cards. Let us notify the changes to the driver,
- * as some drivers used this to restore its orig_* reg domain.
- */
- if (initiator == NL80211_REGDOM_SET_BY_CORE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
- wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, get_last_request());
}
}
@@ -1263,7 +1314,8 @@ static void handle_channel_custom(struct wiphy *wiphy,
if (IS_ERR(reg_rule)) {
REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
chan->center_freq);
- chan->flags = IEEE80211_CHAN_DISABLED;
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
return;
}
@@ -1305,6 +1357,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
enum ieee80211_band band;
unsigned int bands_set = 0;
+ WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG),
+ "wiphy should have REGULATORY_CUSTOM_REG\n");
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!wiphy->bands[band])
continue;
@@ -1320,225 +1376,285 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
}
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
-/* This has the logic which determines when a new request
- * should be ignored. */
-static enum reg_request_treatment
-get_reg_request_treatment(struct wiphy *wiphy,
- struct regulatory_request *pending_request)
+static void reg_set_request_processed(void)
{
- struct wiphy *last_wiphy = NULL;
+ bool need_more_processing = false;
struct regulatory_request *lr = get_last_request();
- /* All initial requests are respected */
- if (!lr)
- return REG_REQ_OK;
+ lr->processed = true;
- switch (pending_request->initiator) {
- case NL80211_REGDOM_SET_BY_CORE:
- return REG_REQ_OK;
- case NL80211_REGDOM_SET_BY_COUNTRY_IE:
- if (reg_request_cell_base(lr)) {
- /* Trust a Cell base station over the AP's country IE */
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_IGNORE;
- return REG_REQ_ALREADY_SET;
- }
+ spin_lock(&reg_requests_lock);
+ if (!list_empty(&reg_requests_list))
+ need_more_processing = true;
+ spin_unlock(&reg_requests_lock);
- last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
+ cancel_delayed_work(&reg_timeout);
- if (unlikely(!is_an_alpha2(pending_request->alpha2)))
- return -EINVAL;
- if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- if (last_wiphy != wiphy) {
- /*
- * Two cards with two APs claiming different
- * Country IE alpha2s. We could
- * intersect them, but that seems unlikely
- * to be correct. Reject second one for now.
- */
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_IGNORE;
- return REG_REQ_ALREADY_SET;
- }
- /*
- * Two consecutive Country IE hints on the same wiphy.
- * This should be picked up early by the driver/stack
- */
- if (WARN_ON(regdom_changes(pending_request->alpha2)))
- return REG_REQ_OK;
- return REG_REQ_ALREADY_SET;
- }
- return REG_REQ_OK;
- case NL80211_REGDOM_SET_BY_DRIVER:
- if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_OK;
- return REG_REQ_ALREADY_SET;
- }
+ if (need_more_processing)
+ schedule_work(&reg_work);
+}
- /*
- * This would happen if you unplug and plug your card
- * back in or if you add a new device for which the previously
- * loaded card also agrees on the regulatory domain.
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
- !regdom_changes(pending_request->alpha2))
- return REG_REQ_ALREADY_SET;
+/**
+ * reg_process_hint_core - process core regulatory requests
+ * @pending_request: a pending core regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by the regulatory core.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_core(struct regulatory_request *core_request)
+{
+
+ core_request->intersect = false;
+ core_request->processed = false;
+
+ reg_update_last_request(core_request);
+ return reg_call_crda(core_request);
+}
+
+static enum reg_request_treatment
+__reg_process_hint_user(struct regulatory_request *user_request)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (reg_request_cell_base(user_request))
+ return reg_ignore_cell_hint(user_request);
+
+ if (reg_request_cell_base(lr))
+ return REG_REQ_IGNORE;
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
return REG_REQ_INTERSECT;
- case NL80211_REGDOM_SET_BY_USER:
- if (reg_request_cell_base(pending_request))
- return reg_ignore_cell_hint(pending_request);
+ /*
+ * If the user knows better the user should set the regdom
+ * to their country before the IE is picked up
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+ lr->intersect)
+ return REG_REQ_IGNORE;
+ /*
+ * Process user requests only after previous user/driver/core
+ * requests have been processed
+ */
+ if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
+ lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+ lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
+ regdom_changes(lr->alpha2))
+ return REG_REQ_IGNORE;
- if (reg_request_cell_base(lr))
- return REG_REQ_IGNORE;
+ if (!regdom_changes(user_request->alpha2))
+ return REG_REQ_ALREADY_SET;
- if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
- return REG_REQ_INTERSECT;
- /*
- * If the user knows better the user should set the regdom
- * to their country before the IE is picked up
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
- lr->intersect)
- return REG_REQ_IGNORE;
- /*
- * Process user requests only after previous user/driver/core
- * requests have been processed
- */
- if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
- lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
- regdom_changes(lr->alpha2))
- return REG_REQ_IGNORE;
+ return REG_REQ_OK;
+}
- if (!regdom_changes(pending_request->alpha2))
- return REG_REQ_ALREADY_SET;
+/**
+ * reg_process_hint_user - process user regulatory requests
+ * @user_request: a pending user regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request initiated by userspace.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_user(struct regulatory_request *user_request)
+{
+ enum reg_request_treatment treatment;
- return REG_REQ_OK;
+ treatment = __reg_process_hint_user(user_request);
+ if (treatment == REG_REQ_IGNORE ||
+ treatment == REG_REQ_ALREADY_SET) {
+ kfree(user_request);
+ return treatment;
}
- return REG_REQ_IGNORE;
+ user_request->intersect = treatment == REG_REQ_INTERSECT;
+ user_request->processed = false;
+
+ reg_update_last_request(user_request);
+
+ user_alpha2[0] = user_request->alpha2[0];
+ user_alpha2[1] = user_request->alpha2[1];
+
+ return reg_call_crda(user_request);
}
-static void reg_set_request_processed(void)
+static enum reg_request_treatment
+__reg_process_hint_driver(struct regulatory_request *driver_request)
{
- bool need_more_processing = false;
struct regulatory_request *lr = get_last_request();
- lr->processed = true;
-
- spin_lock(&reg_requests_lock);
- if (!list_empty(&reg_requests_list))
- need_more_processing = true;
- spin_unlock(&reg_requests_lock);
+ if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
+ if (regdom_changes(driver_request->alpha2))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+ }
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
- cancel_delayed_work(&reg_timeout);
+ /*
+ * This would happen if you unplug and plug your card
+ * back in or if you add a new device for which the previously
+ * loaded card also agrees on the regulatory domain.
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ !regdom_changes(driver_request->alpha2))
+ return REG_REQ_ALREADY_SET;
- if (need_more_processing)
- schedule_work(&reg_work);
+ return REG_REQ_INTERSECT;
}
/**
- * __regulatory_hint - hint to the wireless core a regulatory domain
- * @wiphy: if the hint comes from country information from an AP, this
- * is required to be set to the wiphy that received the information
- * @pending_request: the regulatory request currently being processed
+ * reg_process_hint_driver - process driver regulatory requests
+ * @driver_request: a pending driver regulatory request
*
- * The Wireless subsystem can use this function to hint to the wireless core
- * what it believes should be the current regulatory domain.
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by an 802.11 driver.
*
* Returns one of the different reg request treatment values.
*/
static enum reg_request_treatment
-__regulatory_hint(struct wiphy *wiphy,
- struct regulatory_request *pending_request)
+reg_process_hint_driver(struct wiphy *wiphy,
+ struct regulatory_request *driver_request)
{
const struct ieee80211_regdomain *regd;
- bool intersect = false;
enum reg_request_treatment treatment;
- struct regulatory_request *lr;
- treatment = get_reg_request_treatment(wiphy, pending_request);
+ treatment = __reg_process_hint_driver(driver_request);
switch (treatment) {
- case REG_REQ_INTERSECT:
- if (pending_request->initiator ==
- NL80211_REGDOM_SET_BY_DRIVER) {
- regd = reg_copy_regd(get_cfg80211_regdom());
- if (IS_ERR(regd)) {
- kfree(pending_request);
- return PTR_ERR(regd);
- }
- rcu_assign_pointer(wiphy->regd, regd);
- }
- intersect = true;
- break;
case REG_REQ_OK:
break;
- default:
- /*
- * If the regulatory domain being requested by the
- * driver has already been set just copy it to the
- * wiphy
- */
- if (treatment == REG_REQ_ALREADY_SET &&
- pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
- regd = reg_copy_regd(get_cfg80211_regdom());
- if (IS_ERR(regd)) {
- kfree(pending_request);
- return REG_REQ_IGNORE;
- }
- treatment = REG_REQ_ALREADY_SET;
- rcu_assign_pointer(wiphy->regd, regd);
- goto new_request;
- }
- kfree(pending_request);
+ case REG_REQ_IGNORE:
+ kfree(driver_request);
return treatment;
+ case REG_REQ_INTERSECT:
+ /* fall through */
+ case REG_REQ_ALREADY_SET:
+ regd = reg_copy_regd(get_cfg80211_regdom());
+ if (IS_ERR(regd)) {
+ kfree(driver_request);
+ return REG_REQ_IGNORE;
+ }
+ rcu_assign_pointer(wiphy->regd, regd);
}
-new_request:
- lr = get_last_request();
- if (lr != &core_request_world && lr)
- kfree_rcu(lr, rcu_head);
- pending_request->intersect = intersect;
- pending_request->processed = false;
- rcu_assign_pointer(last_request, pending_request);
- lr = pending_request;
+ driver_request->intersect = treatment == REG_REQ_INTERSECT;
+ driver_request->processed = false;
- pending_request = NULL;
+ reg_update_last_request(driver_request);
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER) {
- user_alpha2[0] = lr->alpha2[0];
- user_alpha2[1] = lr->alpha2[1];
+ /*
+ * Since CRDA will not be called in this case as we already
+ * have applied the requested regulatory domain before we just
+ * inform userspace we have processed the request
+ */
+ if (treatment == REG_REQ_ALREADY_SET) {
+ nl80211_send_reg_change_event(driver_request);
+ reg_set_request_processed();
+ return treatment;
}
- /* When r == REG_REQ_INTERSECT we do need to call CRDA */
- if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) {
+ return reg_call_crda(driver_request);
+}
+
+static enum reg_request_treatment
+__reg_process_hint_country_ie(struct wiphy *wiphy,
+ struct regulatory_request *country_ie_request)
+{
+ struct wiphy *last_wiphy = NULL;
+ struct regulatory_request *lr = get_last_request();
+
+ if (reg_request_cell_base(lr)) {
+ /* Trust a Cell base station over the AP's country IE */
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
+ } else {
+ if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE)
+ return REG_REQ_IGNORE;
+ }
+
+ if (unlikely(!is_an_alpha2(country_ie_request->alpha2)))
+ return -EINVAL;
+
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)
+ return REG_REQ_OK;
+
+ last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+
+ if (last_wiphy != wiphy) {
/*
- * Since CRDA will not be called in this case as we already
- * have applied the requested regulatory domain before we just
- * inform userspace we have processed the request
+ * Two cards with two APs claiming different
+ * Country IE alpha2s. We could
+ * intersect them, but that seems unlikely
+ * to be correct. Reject second one for now.
*/
- if (treatment == REG_REQ_ALREADY_SET) {
- nl80211_send_reg_change_event(lr);
- reg_set_request_processed();
- }
- return treatment;
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
}
+ /*
+ * Two consecutive Country IE hints on the same wiphy.
+ * This should be picked up early by the driver/stack
+ */
+ if (WARN_ON(regdom_changes(country_ie_request->alpha2)))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+}
+
+/**
+ * reg_process_hint_country_ie - process regulatory requests from country IEs
+ * @country_ie_request: a regulatory request from a country IE
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by a country Information Element.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_country_ie(struct wiphy *wiphy,
+ struct regulatory_request *country_ie_request)
+{
+ enum reg_request_treatment treatment;
+
+ treatment = __reg_process_hint_country_ie(wiphy, country_ie_request);
- if (call_crda(lr->alpha2))
+ switch (treatment) {
+ case REG_REQ_OK:
+ break;
+ case REG_REQ_IGNORE:
+ /* fall through */
+ case REG_REQ_ALREADY_SET:
+ kfree(country_ie_request);
+ return treatment;
+ case REG_REQ_INTERSECT:
+ kfree(country_ie_request);
+ /*
+ * This doesn't happen yet, not sure we
+ * ever want to support it for this case.
+ */
+ WARN_ONCE(1, "Unexpected intersection for country IEs");
return REG_REQ_IGNORE;
- return REG_REQ_OK;
+ }
+
+ country_ie_request->intersect = false;
+ country_ie_request->processed = false;
+
+ reg_update_last_request(country_ie_request);
+
+ return reg_call_crda(country_ie_request);
}
/* This processes *all* regulatory hints */
-static void reg_process_hint(struct regulatory_request *reg_request,
- enum nl80211_reg_initiator reg_initiator)
+static void reg_process_hint(struct regulatory_request *reg_request)
{
struct wiphy *wiphy = NULL;
+ enum reg_request_treatment treatment;
if (WARN_ON(!reg_request->alpha2))
return;
@@ -1546,23 +1662,37 @@ static void reg_process_hint(struct regulatory_request *reg_request,
if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
- if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
+ if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
kfree(reg_request);
return;
}
- switch (__regulatory_hint(wiphy, reg_request)) {
- case REG_REQ_ALREADY_SET:
- /* This is required so that the orig_* parameters are saved */
- if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
- wiphy_update_regulatory(wiphy, reg_initiator);
+ switch (reg_request->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ reg_process_hint_core(reg_request);
+ return;
+ case NL80211_REGDOM_SET_BY_USER:
+ treatment = reg_process_hint_user(reg_request);
+ if (treatment == REG_REQ_OK ||
+ treatment == REG_REQ_ALREADY_SET)
+ return;
+ schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
+ return;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ treatment = reg_process_hint_driver(wiphy, reg_request);
break;
- default:
- if (reg_initiator == NL80211_REGDOM_SET_BY_USER)
- schedule_delayed_work(&reg_timeout,
- msecs_to_jiffies(3142));
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ treatment = reg_process_hint_country_ie(wiphy, reg_request);
break;
+ default:
+ WARN(1, "invalid initiator %d\n", reg_request->initiator);
+ return;
}
+
+ /* This is required so that the orig_* parameters are saved */
+ if (treatment == REG_REQ_ALREADY_SET && wiphy &&
+ wiphy->regulatory_flags & REGULATORY_STRICT_REG)
+ wiphy_update_regulatory(wiphy, reg_request->initiator);
}
/*
@@ -1596,7 +1726,7 @@ static void reg_process_pending_hints(void)
spin_unlock(&reg_requests_lock);
- reg_process_hint(reg_request, reg_request->initiator);
+ reg_process_hint(reg_request);
}
/* Processes beacon hints -- this has nothing to do with country IEs */
@@ -1888,7 +2018,7 @@ static void restore_regulatory_settings(bool reset_user)
world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
- if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+ if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
restore_custom_reg_settings(&rdev->wiphy);
}
@@ -2016,7 +2146,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
}
}
-bool reg_supported_dfs_region(u8 dfs_region)
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
{
switch (dfs_region) {
case NL80211_DFS_UNSET:
@@ -2031,27 +2161,6 @@ bool reg_supported_dfs_region(u8 dfs_region)
}
}
-static void print_dfs_region(u8 dfs_region)
-{
- if (!dfs_region)
- return;
-
- switch (dfs_region) {
- case NL80211_DFS_FCC:
- pr_info(" DFS Master region FCC");
- break;
- case NL80211_DFS_ETSI:
- pr_info(" DFS Master region ETSI");
- break;
- case NL80211_DFS_JP:
- pr_info(" DFS Master region JP");
- break;
- default:
- pr_info(" DFS Master region Unknown");
- break;
- }
-}
-
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
struct regulatory_request *lr = get_last_request();
@@ -2083,7 +2192,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
}
}
- print_dfs_region(rd->dfs_region);
+ pr_info(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));
print_rd_rules(rd);
}
@@ -2093,48 +2202,60 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
print_rd_rules(rd);
}
-/* Takes ownership of rd only if it doesn't fail */
-static int __set_regdom(const struct ieee80211_regdomain *rd)
+static int reg_set_rd_core(const struct ieee80211_regdomain *rd)
+{
+ if (!is_world_regdom(rd->alpha2))
+ return -EINVAL;
+ update_world_regdomain(rd);
+ return 0;
+}
+
+static int reg_set_rd_user(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *user_request)
{
- const struct ieee80211_regdomain *regd;
const struct ieee80211_regdomain *intersected_rd = NULL;
- struct wiphy *request_wiphy;
- struct regulatory_request *lr = get_last_request();
- /* Some basic sanity checks first */
+ if (is_world_regdom(rd->alpha2))
+ return -EINVAL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
- if (!reg_is_valid_request(rd->alpha2))
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected:\n");
+ print_regdomain_info(rd);
return -EINVAL;
+ }
- if (is_world_regdom(rd->alpha2)) {
- update_world_regdomain(rd);
+ if (!user_request->intersect) {
+ reset_regdomains(false, rd);
return 0;
}
- if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
- !is_unknown_alpha2(rd->alpha2))
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
return -EINVAL;
- /*
- * Lets only bother proceeding on the same alpha2 if the current
- * rd is non static (it means CRDA was present and was used last)
- * and the pending request came in from a country IE
- */
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- /*
- * If someone else asked us to change the rd lets only bother
- * checking if the alpha2 changes if CRDA was already called
- */
- if (!regdom_changes(rd->alpha2))
- return -EALREADY;
- }
+ kfree(rd);
+ rd = NULL;
+ reset_regdomains(false, intersected_rd);
- /*
- * Now lets set the regulatory domain, update all driver channels
- * and finally inform them of what we have done, in case they want
- * to review or adjust their own settings based on their own
- * internal EEPROM data
- */
+ return 0;
+}
+
+static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *driver_request)
+{
+ const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+ const struct ieee80211_regdomain *tmp;
+ struct wiphy *request_wiphy;
+
+ if (is_world_regdom(rd->alpha2))
+ return -EINVAL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
if (!is_valid_rd(rd)) {
pr_err("Invalid regulatory domain detected:\n");
@@ -2142,29 +2263,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return -EINVAL;
}
- request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
- if (!request_wiphy &&
- (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
+ request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
+ if (!request_wiphy) {
schedule_delayed_work(&reg_timeout, 0);
return -ENODEV;
}
- if (!lr->intersect) {
- if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
- reset_regdomains(false, rd);
- return 0;
- }
-
- /*
- * For a driver hint, lets copy the regulatory domain the
- * driver wanted to the wiphy to deal with conflicts
- */
-
- /*
- * Userspace could have sent two replies with only
- * one kernel request.
- */
+ if (!driver_request->intersect) {
if (request_wiphy->regd)
return -EALREADY;
@@ -2177,38 +2282,59 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return 0;
}
- /* Intersection requires a bit more work */
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
+ return -EINVAL;
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
- if (!intersected_rd)
- return -EINVAL;
+ /*
+ * We can trash what CRDA provided now.
+ * However if a driver requested this specific regulatory
+ * domain we keep it for its private use
+ */
+ tmp = get_wiphy_regdom(request_wiphy);
+ rcu_assign_pointer(request_wiphy->regd, rd);
+ rcu_free_regdom(tmp);
- /*
- * We can trash what CRDA provided now.
- * However if a driver requested this specific regulatory
- * domain we keep it for its private use
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
- const struct ieee80211_regdomain *tmp;
+ rd = NULL;
- tmp = get_wiphy_regdom(request_wiphy);
- rcu_assign_pointer(request_wiphy->regd, rd);
- rcu_free_regdom(tmp);
- } else {
- kfree(rd);
- }
+ reset_regdomains(false, intersected_rd);
- rd = NULL;
+ return 0;
+}
- reset_regdomains(false, intersected_rd);
+static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *country_ie_request)
+{
+ struct wiphy *request_wiphy;
- return 0;
+ if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
+ !is_unknown_alpha2(rd->alpha2))
+ return -EINVAL;
+
+ /*
+ * Lets only bother proceeding on the same alpha2 if the current
+ * rd is non static (it means CRDA was present and was used last)
+ * and the pending request came in from a country IE
+ */
+
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected:\n");
+ print_regdomain_info(rd);
+ return -EINVAL;
}
- return -EINVAL;
-}
+ request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx);
+ if (!request_wiphy) {
+ schedule_delayed_work(&reg_timeout, 0);
+ return -ENODEV;
+ }
+
+ if (country_ie_request->intersect)
+ return -EINVAL;
+ reset_regdomains(false, rd);
+ return 0;
+}
/*
* Use this call to set the current regulatory domain. Conflicts with
@@ -2220,10 +2346,32 @@ int set_regdom(const struct ieee80211_regdomain *rd)
struct regulatory_request *lr;
int r;
+ if (!reg_is_valid_request(rd->alpha2)) {
+ kfree(rd);
+ return -EINVAL;
+ }
+
lr = get_last_request();
/* Note that this doesn't update the wiphys, this is done below */
- r = __set_regdom(rd);
+ switch (lr->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ r = reg_set_rd_core(rd);
+ break;
+ case NL80211_REGDOM_SET_BY_USER:
+ r = reg_set_rd_user(rd, lr);
+ break;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ r = reg_set_rd_driver(rd, lr);
+ break;
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ r = reg_set_rd_country_ie(rd, lr);
+ break;
+ default:
+ WARN(1, "invalid initiator %d\n", lr->initiator);
+ return -EINVAL;
+ }
+
if (r) {
if (r == -EALREADY)
reg_set_request_processed();
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 9677e3c13da..cc4c2c0a672 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -18,8 +18,9 @@
extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
+bool reg_is_valid_request(const char *alpha2);
bool is_world_regdom(const char *alpha2);
-bool reg_supported_dfs_region(u8 dfs_region);
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region);
int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index ba5f0d6614d..f7aa7a72d9b 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1653,9 +1653,8 @@ TRACE_EVENT(rdev_cancel_remain_on_channel,
TRACE_EVENT(rdev_mgmt_tx,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, bool no_cck, bool dont_wait_for_ack),
- TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack),
+ struct cfg80211_mgmt_tx_params *params),
+ TP_ARGS(wiphy, wdev, params),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
@@ -1668,11 +1667,11 @@ TRACE_EVENT(rdev_mgmt_tx,
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
- CHAN_ASSIGN(chan);
- __entry->offchan = offchan;
- __entry->wait = wait;
- __entry->no_cck = no_cck;
- __entry->dont_wait_for_ack = dont_wait_for_ack;
+ CHAN_ASSIGN(params->chan);
+ __entry->offchan = params->offchan;
+ __entry->wait = params->wait;
+ __entry->no_cck = params->no_cck;
+ __entry->dont_wait_for_ack = params->dont_wait_for_ack;
),
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s,"
" wait: %u, no cck: %s, dont wait for ack: %s",