From 8fe02e167efa8ed4a4503a5eedc0f49fcb7e3eb9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 21 Oct 2013 19:22:25 +0200 Subject: cfg80211: consolidate passive-scan and no-ibss flags These two flags are used for the same purpose, just combine them into a no-ir flag to annotate no initiating radiation is allowed. Old userspace sending either flag will have it treated as the no-ir flag. To be considerate to older userspace we also send both the no-ir flag and the old no-ibss flags. Newer userspace will have to be aware of older kernels. Update all places in the tree using these flags with the following semantic patch: @@ @@ -NL80211_RRF_PASSIVE_SCAN +NL80211_RRF_NO_IR @@ @@ -NL80211_RRF_NO_IBSS +NL80211_RRF_NO_IR @@ @@ -IEEE80211_CHAN_PASSIVE_SCAN +IEEE80211_CHAN_NO_IR @@ @@ -IEEE80211_CHAN_NO_IBSS +IEEE80211_CHAN_NO_IR @@ @@ -NL80211_RRF_NO_IR | NL80211_RRF_NO_IR +NL80211_RRF_NO_IR @@ @@ -IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_IR +IEEE80211_CHAN_NO_IR @@ @@ -(NL80211_RRF_NO_IR) +NL80211_RRF_NO_IR @@ @@ -(IEEE80211_CHAN_NO_IR) +IEEE80211_CHAN_NO_IR Along with some hand-optimisations in documentation, to remove duplicates and to fix some indentation. Signed-off-by: Luis R. Rodriguez [do all the driver updates in one go] Signed-off-by: Johannes Berg --- net/wireless/chan.c | 3 +-- net/wireless/genregdb.awk | 7 +++++-- net/wireless/ibss.c | 4 ++-- net/wireless/mesh.c | 3 +-- net/wireless/nl80211.c | 12 ++++++------ net/wireless/reg.c | 33 ++++++++++----------------------- 6 files changed, 25 insertions(+), 37 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 9b8cc877eb19..344966496b70 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -467,8 +467,7 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, res = cfg80211_chandef_usable(wiphy, chandef, IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_PASSIVE_SCAN | - IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR); trace_cfg80211_return_bool(res); diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index 42ed274e81f4..c808619ac9c6 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -107,10 +107,13 @@ 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 diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 9d797df56649..f79105712949 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 0553fd4d85ae..b0e1869de7de 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/nl80211.c b/net/wireless/nl80211.c index a7f4e7902104..41af3a0e9961 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -545,12 +545,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; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7da67fd0b418..e4e3337ba296 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -163,35 +163,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), @@ -698,10 +692,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) @@ -1066,13 +1058,8 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, 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; } -- cgit v1.2.3 From 40d1ba63ff4ae1a73b0042202b54b688ada469be Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Tue, 5 Nov 2013 14:48:47 +0100 Subject: cfg80211: add helper functions for start/end freq Add helper fuctions for start/end freq. Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/chan.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 344966496b70..1d25a462b145 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); @@ -337,13 +358,8 @@ static bool cfg80211_secondary_chans_ok(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); -- cgit v1.2.3 From fe7c3a1f20a419d86d3f90316d8efc2d04f3f0ed Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Tue, 5 Nov 2013 14:48:48 +0100 Subject: cfg80211: DFS check chandef usable before CAC Check chandef we get in CAC request is usable for CAC. All channels have to be DFS channels. Allow DFS_USABLE and DFS_AVAILABLE channels mix. At least one channel has to be DFS_USABLE (require CAC). Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/chan.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 13 +++++++++ net/wireless/nl80211.c | 2 +- 3 files changed, 88 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 1d25a462b145..96c97800e247 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -351,6 +351,80 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_chandef_dfs_required); +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; + + 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_secondary_chans_ok(struct wiphy *wiphy, u32 center_freq, u32 bandwidth, u32 prohibited_flags) diff --git a/net/wireless/core.h b/net/wireless/core.h index eb0f7a3a25a9..2888867ee7c5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -382,6 +382,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/nl80211.c b/net/wireless/nl80211.c index 41af3a0e9961..e2bb4276af1a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5651,7 +5651,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) -- cgit v1.2.3 From 222ea5819901ed174db4df2e26aa5e982f857845 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:00 -0800 Subject: cfg80211: force WIPHY_FLAG_CUSTOM_REGULATORY on wiphy_apply_custom_regulatory() wiphy_apply_custom_regulatory() implies WIPHY_FLAG_CUSTOM_REGULATORY but we never enforced it, do that now and warn if the driver didn't set it. All drivers should be following this today already. Having WIPHY_FLAG_CUSTOM_REGULATORY does not however mean you will use wiphy_apply_custom_regulatory() though, you may have your own _orig value set up tools / helpers. The intel drivers are examples of this type of driver. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e4e3337ba296..04b6fe4b7653 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1292,6 +1292,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; + WARN(!(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY), + "wiphy should have WIPHY_FLAG_CUSTOM_REGULATORY\n"); + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; -- cgit v1.2.3 From 1daa37c7ba01bd788148d6a9dc5d6fb491a20931 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:02 -0800 Subject: cfg80211: remove second argument from reg_process_hint() The iniator is already available to us, so use it. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 04b6fe4b7653..dccdfe36a310 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1526,8 +1526,7 @@ new_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; @@ -1537,7 +1536,7 @@ 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; } @@ -1546,10 +1545,10 @@ static void reg_process_hint(struct regulatory_request *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); + wiphy_update_regulatory(wiphy, reg_request->initiator); break; default: - if (reg_initiator == NL80211_REGDOM_SET_BY_USER) + if (reg_request->initiator == NL80211_REGDOM_SET_BY_USER) schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); break; @@ -1587,7 +1586,7 @@ static void reg_process_pending_hints(void) spin_unlock(®_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 */ -- cgit v1.2.3 From 0f0094b3c7b8fef51f1ea6a0abc08b99d1444506 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 25 Oct 2013 12:46:44 +0200 Subject: cfg80211: add missing break in cfg80211_get_chan_state() Improve readability of the function by adding the break, there's no functional impact but it's confusing to fall through. Signed-off-by: Johannes Berg --- net/wireless/chan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 96c97800e247..3b6daf8b47d9 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -599,6 +599,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) { -- cgit v1.2.3 From 00c3a6ed649c3305b52ff51e187717365aa39d4a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 26 Oct 2013 17:14:38 +0200 Subject: cfg80211: don't allow drivers to unset NL80211_FEATURE_SCAN_FLUSH As the flag is entirely implemented in cfg80211, it should have been a global feature flag (which I believe didn't exist at the time). However, there's no reason to allow drivers to unset the flag, so don't allow it and remove the validation of NL80211_SCAN_FLAG_FLUSH. Signed-off-by: Johannes Berg --- net/wireless/core.c | 4 ++-- net/wireless/nl80211.c | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index aff959e5a1b3..9dca0925a1a9 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) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e2bb4276af1a..1ad11de6dd2f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5342,10 +5342,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; } @@ -5585,10 +5583,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; } -- cgit v1.2.3 From d2859df5e7f00469011482d850fba652517a2eab Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 6 Nov 2013 13:55:51 +0100 Subject: cfg80211/mac80211: DFS setup chandef for cac event To report channel width correctly we have to send correct channel parameters from mac80211 when calling cfg80211_cac_event(). This is required in case of using channel width higher than 20MHz and we have to set correct dfs channel state after CAC (NL80211_DFS_AVAILABLE). Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/mlme.c | 8 +++----- net/wireless/nl80211.c | 4 ++-- net/wireless/nl80211.h | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 6a6b1c8e907d..31f541f7e4ea 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -763,12 +763,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 +779,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 +794,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 1ad11de6dd2f..04fa8bb1b4bb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2168,7 +2168,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)); @@ -10874,7 +10874,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 2c0f2b3c07cb..b1b231324e10 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); -- cgit v1.2.3 From 6bc54fbcee6836f08355fcca76549c22ad2c2940 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 6 Nov 2013 13:55:53 +0100 Subject: cfg80211: allow beaconing after DFS CAC Allow beconing after we pass Channel Availability Check (CAC). Allow non-DFS and DFS channels mix. All DFS channels have to be in NL80211_DFS_AVAILABLE state (pass CAC). Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- net/wireless/chan.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 11 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 3b6daf8b47d9..78559b5bbd1f 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -425,9 +425,9 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, } -static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, - u32 center_freq, u32 bandwidth, - u32 prohibited_flags) +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; @@ -435,18 +435,75 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, 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; +} + + +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; - /* check for the other flags */ - if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR) + 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; } @@ -552,13 +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_NO_IR | - 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; -- cgit v1.2.3 From af9b223551634ce1881de258169f3a1ea686a865 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 28 Oct 2013 03:19:45 +0100 Subject: cfg80211: add function helpers to genregdb.awk This has no functional change, this just lets us reuse helpers at a later time. Signed-off-by: Luis R. Rodriguez Acked-by: John W. Linville Signed-off-by: Johannes Berg --- net/wireless/genregdb.awk | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index c808619ac9c6..42cd3fd2aa14 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®dom_" country ",\n" } -active && /^[ \t]*\(/ { +function parse_reg_rule() +{ start = $1 sub(/\(/, "", start) end = $3 @@ -120,7 +113,8 @@ active && /^[ \t]*\(/ { rules++ } -active && /^[ \t]*$/ { +function print_tail_country() +{ active = 0 printf "\t},\n" printf "\t.n_reg_rules = %d\n", rules @@ -128,6 +122,26 @@ 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 { print regdb "};" print "" -- cgit v1.2.3 From ae689390b09d1f5b274c83dbbd3836fedb692e31 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 28 Oct 2013 03:19:46 +0100 Subject: cfg80211: fix parsing when db.txt ends on a rule If genregdb.awk assumes the file will end with an extra empty line or a comment line. This is could not be true so just address this. Signed-off-by: Luis R. Rodriguez Acked-by: John W. Linville Signed-off-by: Johannes Berg --- net/wireless/genregdb.awk | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index 42cd3fd2aa14..9a8217d2a908 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -143,6 +143,8 @@ active && /^[ \t]*$/ { } END { + if (active) + print_tail_country() print regdb "};" print "" print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" -- cgit v1.2.3 From cc493e4f5296f4da111f25ea4a216bb77270ccc6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 6 Nov 2013 17:54:44 +0100 Subject: cfg80211: enforce disabling channels by custom or strict settings If a custom regulatory domain is passed and if a rule for a channel indicates it should be disabled that channel should always remain disabled as per its documentation and design. Likewise if WIPHY_FLAG_STRICT_REGULATORY flag is set and a regulatory_hint() is issued if a channel is disabled that channel should remain disabled. Without this change only drivers that set the _orig flags appropriately on their own would ensure disallowed channels remaind disabled. This helps drivers save code by relying on the APIS provided to entrust channels that should not be enabled be respected by only having to use wiphy_apply_custom_regulatory() or regulatory_hint() with the WIPHY_FLAG_STRICT_REGULATORY set. If wiphy_apply_custom_regulatory() is used together with WIPHY_FLAG_STRICT_REGULATORY and a regulatory_hint() issued later, the incoming regulatory domain can override previously set _orig parameters from the initial custom regulatory setting. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index dccdfe36a310..b4b16871a56e 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -846,8 +846,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->flags & WIPHY_FLAG_STRICT_REGULATORY) { + 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; } @@ -1250,7 +1260,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; } -- cgit v1.2.3 From e438768ff9b22c83a968e14b79e8c83128e8bfe4 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:01 -0800 Subject: cfg80211: check regulatory request alpha2 early Currently nl80211 allows userspace to send the kernel a bogus regulatory domain with at most 32 rules set and it won't reject it until after its allocated memory. Let's be smart about it and take advantage that the last_request is now available under RTNL and check if the alpha2 matches an expected request and reject any bogus userspace requests prior to hitting the memory allocator. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 3 +++ net/wireless/reg.c | 2 +- net/wireless/reg.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 04fa8bb1b4bb..7b73132910b7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5100,6 +5100,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); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b4b16871a56e..d8f047aadd49 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -450,7 +450,7 @@ static int call_crda(const char *alpha2) return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); } -static bool reg_is_valid_request(const char *alpha2) +bool reg_is_valid_request(const char *alpha2) { struct regulatory_request *lr = get_last_request(); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 9677e3c13da9..b4076babaf47 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -18,6 +18,7 @@ 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); -- cgit v1.2.3 From b3eb7f3f592c0a5ae96c0cf53037f1a1d7eb4a85 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:03 -0800 Subject: cfg80211: processing core regulatory hints on its own This makes the code path easier to read for the core case. Signed-off-by: Luis R. Rodriguez [add warning to default case in switch to avoid compile warning] Signed-off-by: Johannes Berg --- net/wireless/reg.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d8f047aadd49..2b7ab01b2616 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1337,7 +1337,7 @@ get_reg_request_treatment(struct wiphy *wiphy, switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: - return REG_REQ_OK; + return REG_REQ_IGNORE; case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (reg_request_cell_base(lr)) { /* Trust a Cell base station over the AP's country IE */ @@ -1442,6 +1442,33 @@ static void reg_set_request_processed(void) schedule_work(®_work); } +/** + * 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) +{ + struct regulatory_request *lr; + + lr = get_last_request(); + if (lr != &core_request_world && lr) + kfree_rcu(lr, rcu_head); + + core_request->intersect = false; + core_request->processed = false; + rcu_assign_pointer(last_request, core_request); + + if (call_crda(core_request->alpha2)) + return REG_REQ_IGNORE; + return REG_REQ_OK; +} + /** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this @@ -1540,6 +1567,7 @@ new_request: 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; @@ -1552,7 +1580,21 @@ static void reg_process_hint(struct regulatory_request *reg_request) return; } - switch (__regulatory_hint(wiphy, reg_request)) { + switch (reg_request->initiator) { + case NL80211_REGDOM_SET_BY_CORE: + reg_process_hint_core(reg_request); + return; + case NL80211_REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + treatment = __regulatory_hint(wiphy, reg_request); + break; + default: + WARN(1, "invalid initiator %d\n", reg_request->initiator); + return; + } + + switch (treatment) { case REG_REQ_ALREADY_SET: /* This is required so that the orig_* parameters are saved */ if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) -- cgit v1.2.3 From 0d97a61917d8c00adb9e445ec23ee09150d720a4 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:04 -0800 Subject: cfg80211: process user regulatory requests on its own This makes the code path easier to read and lets us split out some functionality that is only user specific, that makes it easier to read the other types of requests. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 114 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 81 insertions(+), 33 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2b7ab01b2616..1bd590c2ed7f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1337,6 +1337,7 @@ get_reg_request_treatment(struct wiphy *wiphy, switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: return REG_REQ_IGNORE; case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (reg_request_cell_base(lr)) { @@ -1388,36 +1389,6 @@ get_reg_request_treatment(struct wiphy *wiphy, return REG_REQ_ALREADY_SET; 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 (reg_request_cell_base(lr)) - return REG_REQ_IGNORE; - - 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; - - if (!regdom_changes(pending_request->alpha2)) - return REG_REQ_ALREADY_SET; - - return REG_REQ_OK; } return REG_REQ_IGNORE; @@ -1469,6 +1440,80 @@ reg_process_hint_core(struct regulatory_request *core_request) return REG_REQ_OK; } +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; + /* + * 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 (!regdom_changes(user_request->alpha2)) + return REG_REQ_ALREADY_SET; + + return REG_REQ_OK; +} + +/** + * 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; + struct regulatory_request *lr; + + treatment = __reg_process_hint_user(user_request); + if (treatment == REG_REQ_IGNORE || + treatment == REG_REQ_ALREADY_SET) { + kfree(user_request); + return treatment; + } + + lr = get_last_request(); + if (lr != &core_request_world && lr) + kfree_rcu(lr, rcu_head); + + user_request->intersect = treatment == REG_REQ_INTERSECT; + user_request->processed = false; + rcu_assign_pointer(last_request, user_request); + + user_alpha2[0] = user_request->alpha2[0]; + user_alpha2[1] = user_request->alpha2[1]; + + if (call_crda(user_request->alpha2)) + return REG_REQ_IGNORE; + return REG_REQ_OK; +} + /** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this @@ -1585,6 +1630,12 @@ static void reg_process_hint(struct regulatory_request *reg_request) 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(®_timeout, msecs_to_jiffies(3142)); + return; case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_COUNTRY_IE: treatment = __regulatory_hint(wiphy, reg_request); @@ -1601,9 +1652,6 @@ static void reg_process_hint(struct regulatory_request *reg_request) wiphy_update_regulatory(wiphy, reg_request->initiator); break; default: - if (reg_request->initiator == NL80211_REGDOM_SET_BY_USER) - schedule_delayed_work(®_timeout, - msecs_to_jiffies(3142)); break; } } -- cgit v1.2.3 From 21636c7faae0a980e73f366e164ffaa77a424786 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:05 -0800 Subject: cfg80211: process driver regulatory requests on its own This makes the code easier to read and follow. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 103 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 17 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1bd590c2ed7f..7188ef9f07b6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1338,6 +1338,7 @@ get_reg_request_treatment(struct wiphy *wiphy, switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_DRIVER: return REG_REQ_IGNORE; case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (reg_request_cell_base(lr)) { @@ -1372,23 +1373,6 @@ get_reg_request_treatment(struct wiphy *wiphy, 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; - } - - /* - * 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; - - return REG_REQ_INTERSECT; } return REG_REQ_IGNORE; @@ -1514,6 +1498,89 @@ reg_process_hint_user(struct regulatory_request *user_request) return REG_REQ_OK; } +static enum reg_request_treatment +__reg_process_hint_driver(struct regulatory_request *driver_request) +{ + struct regulatory_request *lr = get_last_request(); + + if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { + if (regdom_changes(driver_request->alpha2)) + return REG_REQ_OK; + return REG_REQ_ALREADY_SET; + } + + /* + * 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; + + return REG_REQ_INTERSECT; +} + +/** + * reg_process_hint_driver - process driver regulatory requests + * @driver_request: a pending driver regulatory request + * + * 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 +reg_process_hint_driver(struct wiphy *wiphy, + struct regulatory_request *driver_request) +{ + const struct ieee80211_regdomain *regd; + enum reg_request_treatment treatment; + struct regulatory_request *lr; + + treatment = __reg_process_hint_driver(driver_request); + + switch (treatment) { + case REG_REQ_OK: + break; + 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); + } + + lr = get_last_request(); + if (lr != &core_request_world && lr) + kfree_rcu(lr, rcu_head); + + driver_request->intersect = treatment == REG_REQ_INTERSECT; + driver_request->processed = false; + rcu_assign_pointer(last_request, driver_request); + + /* + * 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; + } + + if (call_crda(driver_request->alpha2)) + return REG_REQ_IGNORE; + return REG_REQ_OK; +} + /** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this @@ -1637,6 +1704,8 @@ static void reg_process_hint(struct regulatory_request *reg_request) schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); return; case NL80211_REGDOM_SET_BY_DRIVER: + treatment = reg_process_hint_driver(wiphy, reg_request); + break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: treatment = __regulatory_hint(wiphy, reg_request); break; -- cgit v1.2.3 From b23e7a9e6b1f4c3070f9ce59e82b9dff69b11fe1 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:06 -0800 Subject: cfg80211: process country IE regulatory requests on their own This is the last split up of the old unified __regultory_hint() processing set of functionality, it moves the country IE processing all on its own. This makes it easier to follow and read what exactly is going on for the case of processing country IEs. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 201 ++++++++++++++++++----------------------------------- 1 file changed, 68 insertions(+), 133 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7188ef9f07b6..aef39243595a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1322,62 +1322,6 @@ 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) -{ - struct wiphy *last_wiphy = NULL; - struct regulatory_request *lr = get_last_request(); - - /* All initial requests are respected */ - if (!lr) - return REG_REQ_OK; - - switch (pending_request->initiator) { - case NL80211_REGDOM_SET_BY_CORE: - case NL80211_REGDOM_SET_BY_USER: - case NL80211_REGDOM_SET_BY_DRIVER: - return REG_REQ_IGNORE; - 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; - } - - last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); - - 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; - } - - return REG_REQ_IGNORE; -} - static void reg_set_request_processed(void) { bool need_more_processing = false; @@ -1581,96 +1525,92 @@ reg_process_hint_driver(struct wiphy *wiphy, return REG_REQ_OK; } +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; + } + + last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); + + if (unlikely(!is_an_alpha2(country_ie_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(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; + } + return REG_REQ_OK; +} + /** - * __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_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 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 a country Information Element. * * 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_country_ie(struct wiphy *wiphy, + struct regulatory_request *country_ie_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_country_ie(wiphy, country_ie_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: + 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); /* - * If the regulatory domain being requested by the - * driver has already been set just copy it to the - * wiphy + * This doesn't happen yet, not sure we + * ever want to support it for this case. */ - 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); - return treatment; + WARN_ONCE(1, "Unexpected intersection for country IEs"); + return REG_REQ_IGNORE; } -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; - - pending_request = NULL; + country_ie_request->intersect = false; + country_ie_request->processed = false; + rcu_assign_pointer(last_request, country_ie_request); - if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { - user_alpha2[0] = lr->alpha2[0]; - user_alpha2[1] = lr->alpha2[1]; - } - - /* When r == REG_REQ_INTERSECT we do need to call CRDA */ - if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { - /* - * 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(lr); - reg_set_request_processed(); - } - return treatment; - } - - if (call_crda(lr->alpha2)) + if (call_crda(country_ie_request->alpha2)) return REG_REQ_IGNORE; return REG_REQ_OK; } @@ -1707,22 +1647,17 @@ static void reg_process_hint(struct regulatory_request *reg_request) treatment = reg_process_hint_driver(wiphy, reg_request); break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - treatment = __regulatory_hint(wiphy, reg_request); + treatment = reg_process_hint_country_ie(wiphy, reg_request); break; default: WARN(1, "invalid initiator %d\n", reg_request->initiator); return; } - switch (treatment) { - 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_request->initiator); - break; - default: - break; - } + /* This is required so that the orig_* parameters are saved */ + if (treatment == REG_REQ_ALREADY_SET && wiphy && + wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) + wiphy_update_regulatory(wiphy, reg_request->initiator); } /* -- cgit v1.2.3 From 2f1c6c572d7b6cbe2e3c9bbb427769f6c79089bb Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:07 -0800 Subject: cfg80211: process non country IE conflicting first By dealing with non country IE conficts first we can shift the code that deals with the conflict to the left. This has no functional changes. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index aef39243595a..875fffc284c0 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1539,31 +1539,32 @@ __reg_process_hint_country_ie(struct wiphy *wiphy, return REG_REQ_ALREADY_SET; } - last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); - if (unlikely(!is_an_alpha2(country_ie_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(country_ie_request->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_ALREADY_SET; - } + + 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) { /* - * Two consecutive Country IE hints on the same wiphy. - * This should be picked up early by the driver/stack + * 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 (WARN_ON(regdom_changes(country_ie_request->alpha2))) - return REG_REQ_OK; + if (regdom_changes(country_ie_request->alpha2)) + return REG_REQ_IGNORE; return REG_REQ_ALREADY_SET; } - return REG_REQ_OK; + /* + * 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; } /** -- cgit v1.2.3 From 5ad6ef5e06a1c9feb934c13b3a5f5303833fef4e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:08 -0800 Subject: cfg80211: add helper for kfree'ing last_request This is common code, this reduces the chance of making a mistake of how we free it. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 875fffc284c0..6ad7b2968862 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -202,11 +202,20 @@ 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 reset_regdomains(bool full_reset, const struct ieee80211_regdomain *new_regdom) { const struct ieee80211_regdomain *r; - struct regulatory_request *lr; ASSERT_RTNL(); @@ -229,9 +238,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); + reg_kfree_last_request(); rcu_assign_pointer(last_request, &core_request_world); } @@ -1353,14 +1360,11 @@ static void reg_set_request_processed(void) static enum reg_request_treatment reg_process_hint_core(struct regulatory_request *core_request) { - struct regulatory_request *lr; - - lr = get_last_request(); - if (lr != &core_request_world && lr) - kfree_rcu(lr, rcu_head); core_request->intersect = false; core_request->processed = false; + + reg_kfree_last_request(); rcu_assign_pointer(last_request, core_request); if (call_crda(core_request->alpha2)) @@ -1417,7 +1421,6 @@ static enum reg_request_treatment reg_process_hint_user(struct regulatory_request *user_request) { enum reg_request_treatment treatment; - struct regulatory_request *lr; treatment = __reg_process_hint_user(user_request); if (treatment == REG_REQ_IGNORE || @@ -1426,12 +1429,10 @@ reg_process_hint_user(struct regulatory_request *user_request) return treatment; } - lr = get_last_request(); - if (lr != &core_request_world && lr) - kfree_rcu(lr, rcu_head); - user_request->intersect = treatment == REG_REQ_INTERSECT; user_request->processed = false; + + reg_kfree_last_request(); rcu_assign_pointer(last_request, user_request); user_alpha2[0] = user_request->alpha2[0]; @@ -1480,7 +1481,6 @@ reg_process_hint_driver(struct wiphy *wiphy, { const struct ieee80211_regdomain *regd; enum reg_request_treatment treatment; - struct regulatory_request *lr; treatment = __reg_process_hint_driver(driver_request); @@ -1501,12 +1501,11 @@ reg_process_hint_driver(struct wiphy *wiphy, rcu_assign_pointer(wiphy->regd, regd); } - lr = get_last_request(); - if (lr != &core_request_world && lr) - kfree_rcu(lr, rcu_head); driver_request->intersect = treatment == REG_REQ_INTERSECT; driver_request->processed = false; + + reg_kfree_last_request(); rcu_assign_pointer(last_request, driver_request); /* @@ -1581,7 +1580,6 @@ reg_process_hint_country_ie(struct wiphy *wiphy, struct regulatory_request *country_ie_request) { enum reg_request_treatment treatment; - struct regulatory_request *lr; treatment = __reg_process_hint_country_ie(wiphy, country_ie_request); @@ -1603,12 +1601,10 @@ reg_process_hint_country_ie(struct wiphy *wiphy, return REG_REQ_IGNORE; } - lr = get_last_request(); - if (lr != &core_request_world && lr) - kfree_rcu(lr, rcu_head); - country_ie_request->intersect = false; country_ie_request->processed = false; + + reg_kfree_last_request(); rcu_assign_pointer(last_request, country_ie_request); if (call_crda(country_ie_request->alpha2)) -- cgit v1.2.3 From 05f1a3ea2dc0d5ae31c8a3a1588cc21bbd5f8413 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:09 -0800 Subject: cfg80211: add helper for kfree'ing and assigning last_request This enforces proper RCU APIs accross the code. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6ad7b2968862..e93a078e5aed 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -212,6 +212,12 @@ static void reg_kfree_last_request(void) 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) { @@ -238,8 +244,7 @@ static void reset_regdomains(bool full_reset, if (!full_reset) return; - reg_kfree_last_request(); - rcu_assign_pointer(last_request, &core_request_world); + reg_update_last_request(&core_request_world); } /* @@ -1364,8 +1369,7 @@ reg_process_hint_core(struct regulatory_request *core_request) core_request->intersect = false; core_request->processed = false; - reg_kfree_last_request(); - rcu_assign_pointer(last_request, core_request); + reg_update_last_request(core_request); if (call_crda(core_request->alpha2)) return REG_REQ_IGNORE; @@ -1432,8 +1436,7 @@ reg_process_hint_user(struct regulatory_request *user_request) user_request->intersect = treatment == REG_REQ_INTERSECT; user_request->processed = false; - reg_kfree_last_request(); - rcu_assign_pointer(last_request, user_request); + reg_update_last_request(user_request); user_alpha2[0] = user_request->alpha2[0]; user_alpha2[1] = user_request->alpha2[1]; @@ -1505,8 +1508,7 @@ reg_process_hint_driver(struct wiphy *wiphy, driver_request->intersect = treatment == REG_REQ_INTERSECT; driver_request->processed = false; - reg_kfree_last_request(); - rcu_assign_pointer(last_request, driver_request); + reg_update_last_request(driver_request); /* * Since CRDA will not be called in this case as we already @@ -1604,8 +1606,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy, country_ie_request->intersect = false; country_ie_request->processed = false; - reg_kfree_last_request(); - rcu_assign_pointer(last_request, country_ie_request); + reg_update_last_request(country_ie_request); if (call_crda(country_ie_request->alpha2)) return REG_REQ_IGNORE; -- cgit v1.2.3 From fe6631ff093150eabc4ab73b948577e9b3bc5b76 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:10 -0800 Subject: cfg80211: add helper for calling CRDA All the regulatory request process routines use the same pattern. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e93a078e5aed..130cfdaa7f4c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -462,6 +462,14 @@ static int call_crda(const char *alpha2) return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); } +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(); @@ -1371,9 +1379,7 @@ reg_process_hint_core(struct regulatory_request *core_request) reg_update_last_request(core_request); - if (call_crda(core_request->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_OK; + return reg_call_crda(core_request); } static enum reg_request_treatment @@ -1441,9 +1447,7 @@ reg_process_hint_user(struct regulatory_request *user_request) user_alpha2[0] = user_request->alpha2[0]; user_alpha2[1] = user_request->alpha2[1]; - if (call_crda(user_request->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_OK; + return reg_call_crda(user_request); } static enum reg_request_treatment @@ -1521,9 +1525,7 @@ reg_process_hint_driver(struct wiphy *wiphy, return treatment; } - if (call_crda(driver_request->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_OK; + return reg_call_crda(driver_request); } static enum reg_request_treatment @@ -1608,9 +1610,7 @@ reg_process_hint_country_ie(struct wiphy *wiphy, reg_update_last_request(country_ie_request); - if (call_crda(country_ie_request->alpha2)) - return REG_REQ_IGNORE; - return REG_REQ_OK; + return reg_call_crda(country_ie_request); } /* This processes *all* regulatory hints */ -- cgit v1.2.3 From f75c30ef084fb57d3edab6722077e0adf16ed8fd Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:11 -0800 Subject: cfg80211: allow only the core to request to update the world regdom It seems some out of tree drivers were using a regulatory_hint("00") to trigger off the wiphy regulatory notifier, for those cases just setting the WIPHY_FLAG_CUSTOM_REGULATORY would suffice to call the reg_notifier() for a world regulatory domain update. If drivers find other needs for calling the reg_notifier() a proper implemenation is preferred. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 130cfdaa7f4c..cd50bb56c7e7 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2200,6 +2200,8 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EINVAL; if (is_world_regdom(rd->alpha2)) { + if (lr->initiator != NL80211_REGDOM_SET_BY_CORE) + return -EINVAL; update_world_regdomain(rd); return 0; } -- cgit v1.2.3 From 0e3802dbfe53bc27a6bf019d022d743349a8d67d Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:12 -0800 Subject: cfg80211: move core reg_notfier() check to source Drivers that set the WIPHY_FLAG_CUSTOM_REGULATORY skip the core world regulatory domain updates, but do want their reg_notifier() called. Move the check for this closer to the source of the check that detected skipped was required and while at it add a helper for the notifier calling. This has no functional changes. This brings together the place where we call the reg_notifier() will be called. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cd50bb56c7e7..38c252f91086 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1222,14 +1222,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->flags & WIPHY_FLAG_CUSTOM_REGULATORY) + reg_call_notifier(wiphy, lr); return; + } lr->dfs_region = get_cfg80211_regdom()->dfs_region; @@ -1238,9 +1254,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) @@ -1253,15 +1267,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()); } } -- cgit v1.2.3 From db0e066a986e025781dcd4c05dc4cf480ae884b6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:13 -0800 Subject: cfg80211: pass the last_request to __set_regdom() last_request is RCU protected, since we're getting it on set_regdom() we might as well pass it to ensure the same request is being processed, otherwise there is a small race it could have changed. This makes processing of the request atomic. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 38c252f91086..ca11f31b3bec 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2192,12 +2192,12 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) } /* Takes ownership of rd only if it doesn't fail */ -static int __set_regdom(const struct ieee80211_regdomain *rd) +static int __set_regdom(const struct ieee80211_regdomain *rd, + struct regulatory_request *lr) { 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 */ @@ -2323,7 +2323,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) lr = get_last_request(); /* Note that this doesn't update the wiphys, this is done below */ - r = __set_regdom(rd); + r = __set_regdom(rd, lr); if (r) { if (r == -EALREADY) reg_set_request_processed(); -- cgit v1.2.3 From 3b9e5aca461ff3c83fb29ff021af19c127efa351 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:14 -0800 Subject: cfg80211: set core regulatory updates on its own This splits up the core regulatory update to be set on its own helper. This should make it easier to read exactly what type of requests should be expected there. In this case its clear that NL80211_REGDOM_SET_BY_CORE is only used by the core for updating the world regulatory domain. This is consistant with the nl80211.h documentation. Signed-off-by: Luis R. Rodriguez [add warning to default switch case to avoid compiler warning] Signed-off-by: Johannes Berg --- net/wireless/reg.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ca11f31b3bec..9f4f8439e16a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2191,6 +2191,14 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) print_rd_rules(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; +} + /* Takes ownership of rd only if it doesn't fail */ static int __set_regdom(const struct ieee80211_regdomain *rd, struct regulatory_request *lr) @@ -2199,18 +2207,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd, const struct ieee80211_regdomain *intersected_rd = NULL; struct wiphy *request_wiphy; - /* Some basic sanity checks first */ - - if (!reg_is_valid_request(rd->alpha2)) - return -EINVAL; - - if (is_world_regdom(rd->alpha2)) { - if (lr->initiator != NL80211_REGDOM_SET_BY_CORE) - return -EINVAL; - update_world_regdomain(rd); - return 0; - } - if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && !is_unknown_alpha2(rd->alpha2)) return -EINVAL; @@ -2320,10 +2316,28 @@ 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, lr); + switch (lr->initiator) { + case NL80211_REGDOM_SET_BY_CORE: + r = reg_set_rd_core(rd); + break; + case NL80211_REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + r = __set_regdom(rd, lr); + break; + default: + WARN(1, "invalid initiator %d\n", lr->initiator); + return -EINVAL; + } + if (r) { if (r == -EALREADY) reg_set_request_processed(); -- cgit v1.2.3 From 84721d44906f9a4ee8b0d41b31fcc9f45295c3a6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:15 -0800 Subject: cfg80211: set user regulatory updates on its own This splits out the user regulatory update on its own, this helps simplify reading the case. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9f4f8439e16a..1f6abba60274 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2199,6 +2199,39 @@ static int reg_set_rd_core(const struct ieee80211_regdomain *rd) return 0; } +static int reg_set_rd_user(const struct ieee80211_regdomain *rd, + struct regulatory_request *user_request) +{ + const struct ieee80211_regdomain *intersected_rd = NULL; + + 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"); + print_regdomain_info(rd); + return -EINVAL; + } + + if (!user_request->intersect) { + reset_regdomains(false, rd); + return 0; + } + + intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); + if (!intersected_rd) + return -EINVAL; + + kfree(rd); + rd = NULL; + reset_regdomains(false, intersected_rd); + + return 0; +} + /* Takes ownership of rd only if it doesn't fail */ static int __set_regdom(const struct ieee80211_regdomain *rd, struct regulatory_request *lr) @@ -2329,6 +2362,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) 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: case NL80211_REGDOM_SET_BY_COUNTRY_IE: r = __set_regdom(rd, lr); -- cgit v1.2.3 From f5fe3247809815153e9fe88a989c251bd17059b9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:16 -0800 Subject: cfg80211: set driver regulatory updates on its own This splits up the driver regulatory update on its own, this helps simplify the reading the case. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 123 ++++++++++++++++++++++++----------------------------- 1 file changed, 56 insertions(+), 67 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1f6abba60274..b622ab0be552 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2232,38 +2232,19 @@ static int reg_set_rd_user(const struct ieee80211_regdomain *rd, return 0; } -/* Takes ownership of rd only if it doesn't fail */ -static int __set_regdom(const struct ieee80211_regdomain *rd, - struct regulatory_request *lr) +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_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && - !is_unknown_alpha2(rd->alpha2)) + if (is_world_regdom(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 (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; - } - - /* - * 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 - */ + if (!regdom_changes(rd->alpha2)) + return -EALREADY; if (!is_valid_rd(rd)) { pr_err("Invalid regulatory domain detected:\n"); @@ -2271,29 +2252,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(®_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; @@ -2306,38 +2271,60 @@ 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; +} + +/* Takes ownership of rd only if it doesn't fail */ +static int __set_regdom(const struct ieee80211_regdomain *rd, + struct regulatory_request *lr) +{ + struct wiphy *request_wiphy; - reset_regdomains(false, intersected_rd); + if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && + !is_unknown_alpha2(rd->alpha2)) + return -EINVAL; - return 0; + /* + * 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(lr->wiphy_idx); + if (!request_wiphy) { + schedule_delayed_work(®_timeout, 0); + return -ENODEV; + } + if (lr->intersect) + return -EINVAL; + + reset_regdomains(false, rd); + return 0; +} /* * Use this call to set the current regulatory domain. Conflicts with @@ -2365,6 +2352,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) 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 = __set_regdom(rd, lr); break; -- cgit v1.2.3 From 01992406d3be60bbd6e19797385ac452bdc8ecc8 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 5 Nov 2013 09:18:17 -0800 Subject: cfg80211: rename __set_regdom() to reg_set_rd_country_ie() This reflects that case is now completely separated. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b622ab0be552..a75c5eddd25f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2291,9 +2291,8 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, return 0; } -/* Takes ownership of rd only if it doesn't fail */ -static int __set_regdom(const struct ieee80211_regdomain *rd, - struct regulatory_request *lr) +static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd, + struct regulatory_request *country_ie_request) { struct wiphy *request_wiphy; @@ -2313,13 +2312,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd, return -EINVAL; } - request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); + request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx); if (!request_wiphy) { schedule_delayed_work(®_timeout, 0); return -ENODEV; } - if (lr->intersect) + if (country_ie_request->intersect) return -EINVAL; reset_regdomains(false, rd); @@ -2355,7 +2354,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) r = reg_set_rd_driver(rd, lr); break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - r = __set_regdom(rd, lr); + r = reg_set_rd_country_ie(rd, lr); break; default: WARN(1, "invalid initiator %d\n", lr->initiator); -- cgit v1.2.3 From a2f73b6c5db3c272d87eaebb5bed355d75a0f25f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 11 Nov 2013 22:15:29 +0100 Subject: cfg80211: move regulatory flags to their own variable We'll expand this later, this will make it easier to classify and review what things are related to regulatory or not. Coccinelle only missed 4 hits, which I had to do manually, supplying the SmPL in case of merge conflicts. @@ struct wiphy *wiphy; @@ -wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY +wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG @@ expression e; @@ -e->flags |= WIPHY_FLAG_CUSTOM_REGULATORY +e->regulatory_flags |= REGULATORY_CUSTOM_REG @@ struct wiphy *wiphy; @@ -wiphy->flags &= ~WIPHY_FLAG_CUSTOM_REGULATORY +wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG @@ struct wiphy *wiphy; @@ -wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY +wiphy->regulatory_flags & REGULATORY_CUSTOM_REG @@ struct wiphy *wiphy; @@ -wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY +wiphy->regulatory_flags |= REGULATORY_STRICT_REG @@ expression e; @@ -e->flags |= WIPHY_FLAG_STRICT_REGULATORY +e->regulatory_flags |= REGULATORY_STRICT_REG @@ struct wiphy *wiphy; @@ -wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY +wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG @@ struct wiphy *wiphy; @@ -wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY +wiphy->regulatory_flags & REGULATORY_STRICT_REG @@ struct wiphy *wiphy; @@ -wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS +wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS @@ expression e; @@ -e->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS +e->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS @@ struct wiphy *wiphy; @@ -wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS +wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS @@ struct wiphy *wiphy; @@ -wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS +wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS Generated-by: Coccinelle SmPL Cc: Julia Lawall Cc: Peter Senna Tschudin Cc: Mihir Shete Cc: Henri Bahini Cc: Tushnim Bhattacharyya Signed-off-by: Luis R. Rodriguez [fix up whitespace damage, overly long lines] Signed-off-by: Johannes Berg --- net/wireless/core.c | 2 +- net/wireless/reg.c | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/core.c b/net/wireless/core.c index 9dca0925a1a9..fc968c861ee4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -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/reg.c b/net/wireless/reg.c index a75c5eddd25f..e44b4bb20b92 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -868,7 +868,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) { REG_DBG_PRINT("Disabling freq %d MHz for good\n", chan->center_freq); chan->orig_flags |= IEEE80211_CHAN_DISABLED; @@ -895,7 +895,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 @@ -922,12 +922,12 @@ static void handle_channel(struct wiphy *wiphy, if (chan->orig_mpwr) { /* * Devices that have their own custom regulatory domain - * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the + * but also use REGULATORY_STRICT_REG will 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_CUSTOM_REG && + wiphy->regulatory_flags & REGULATORY_STRICT_REG) chan->max_power = chan->max_reg_power; else chan->max_power = min(chan->orig_mpwr, @@ -997,8 +997,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; } @@ -1016,7 +1016,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", @@ -1054,7 +1054,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; @@ -1082,7 +1082,7 @@ 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; @@ -1242,7 +1242,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, * 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->regulatory_flags & REGULATORY_CUSTOM_REG) reg_call_notifier(wiphy, lr); return; } @@ -1328,9 +1328,9 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; - WARN(!(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY), - "wiphy should have WIPHY_FLAG_CUSTOM_REGULATORY\n"); - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + 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]) @@ -1659,7 +1659,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) /* This is required so that the orig_* parameters are saved */ if (treatment == REG_REQ_ALREADY_SET && wiphy && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) + wiphy->regulatory_flags & REGULATORY_STRICT_REG) wiphy_update_regulatory(wiphy, reg_request->initiator); } @@ -1986,7 +1986,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); } -- cgit v1.2.3 From a09a85a013523a8b572dc5732b5c30e0785195f3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 11 Nov 2013 22:15:30 +0100 Subject: cfg80211: add flags to define country IE processing rules 802.11 cards may have different country IE parsing behavioural preferences and vendors may want to support these. These preferences were managed by the REGULATORY_CUSTOM_REG and the REGULATORY_STRICT_REG flags and their combination. Instead of using this existing notation, split out the country IE behavioural preferences as a new flag. This will allow us to add more customizations easily and make the code more maintainable. Cc: Mihir Shete Cc: Henri Bahini Cc: Tushnim Bhattacharyya Signed-off-by: Luis R. Rodriguez [fix up conflicts] Signed-off-by: Johannes Berg --- net/wireless/reg.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e44b4bb20b92..6b3051f6a8a7 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -921,13 +921,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 REGULATORY_STRICT_REG 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->regulatory_flags & REGULATORY_CUSTOM_REG && - wiphy->regulatory_flags & REGULATORY_STRICT_REG) + wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER) chan->max_power = chan->max_reg_power; else chan->max_power = min(chan->orig_mpwr, -- cgit v1.2.3 From 2a901468c221e778af52603e006a53d286e81f90 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 11 Nov 2013 22:15:31 +0100 Subject: cfg80211: add an option to disable processing country IEs Certain vendors may want to disable the processing of country IEs so that they can continue using the regulatory domain the driver or user has set. Currently there is no way to stop the core from processing country IEs, so add support to the core to ignore country IE hints. Cc: Mihir Shete Cc: Henri Bahini Cc: Tushnim Bhattacharyya Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6b3051f6a8a7..067c1f63a1ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1543,6 +1543,9 @@ __reg_process_hint_country_ie(struct wiphy *wiphy, 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))) -- cgit v1.2.3 From 4c7d3982a6e37831382b9ef90aa0dbadc0bf3a22 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 Nov 2013 18:54:02 +0100 Subject: cfg80211: use enum nl80211_dfs_regions for dfs_region everywhere u8 was used in some other places, just stick to the enum, this forces us to express the values that are expected. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- net/wireless/reg.c | 4 ++-- net/wireless/reg.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7b73132910b7..79632edebb6c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5079,7 +5079,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]) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 067c1f63a1ae..2796b622890f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2115,7 +2115,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: @@ -2130,7 +2130,7 @@ bool reg_supported_dfs_region(u8 dfs_region) } } -static void print_dfs_region(u8 dfs_region) +static void print_dfs_region(enum nl80211_dfs_regions dfs_region) { if (!dfs_region) return; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index b4076babaf47..cc4c2c0a6723 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -20,7 +20,7 @@ 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); -- cgit v1.2.3 From adbfb058155dfabe77ea73c303cbd75af7e4d9d3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 Nov 2013 18:54:03 +0100 Subject: cfg80211: intersection dfs regions when intersecting regdomains Only allow DFS to be set if the DFS regions agree. Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2796b622890f..068cb4055a62 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -570,6 +570,20 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, #undef ONE_GHZ_IN_KHZ } +/* + * 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 @@ -701,6 +715,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; } -- cgit v1.2.3 From d7a4b05a2794fc8f2a545272048c7212951c1946 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 Nov 2013 18:54:04 +0100 Subject: cfg80211: distinguish unset DFS region from unknown Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 068cb4055a62..740eccb42069 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2152,6 +2152,9 @@ static void print_dfs_region(enum nl80211_dfs_regions dfs_region) return; switch (dfs_region) { + case NL80211_DFS_UNSET: + pr_info(" DFS Master region unset"); + break; case NL80211_DFS_FCC: pr_info(" DFS Master region FCC"); break; -- cgit v1.2.3 From 3ef121b51164282e9eaa732bf79ff027599867c6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 13 Nov 2013 18:54:05 +0100 Subject: cfg80211: replace print_dfs_region() with reg_dfs_region_str() helper This lets us later reuse the more generic reg_dfs_region_str(). Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 740eccb42069..ec54e1aac8e2 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) @@ -2146,30 +2161,6 @@ bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region) } } -static void print_dfs_region(enum nl80211_dfs_regions dfs_region) -{ - if (!dfs_region) - return; - - switch (dfs_region) { - case NL80211_DFS_UNSET: - pr_info(" DFS Master region unset"); - break; - 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(); @@ -2201,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); } -- cgit v1.2.3 From 01e0daa43f129fc1a6bc6f1197343c0293af866d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 9 Nov 2013 14:57:54 +0100 Subject: cfg80211: fix reporting 5/10 MHz support to user space nla_put_flag needs a real nl80211 attribute id, not a wiphy flag bit. While at it, split 5 and 10 MHz capability flags in case we ever need to support hardware that can only do one of the two. Also move the flag settings to the split-only information so we don't increase the space needed for old userspace. Signed-off-by: Felix Fietkau [change location of flag setting] Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 79632edebb6c..8c83fbb3824f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1228,10 +1228,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; @@ -1560,6 +1556,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; -- cgit v1.2.3 From b176e629402f41f2b984d3aa842ddae23ed5562e Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Mon, 18 Nov 2013 19:06:49 +0200 Subject: cfg80211: aggregate mgmt_tx parameters into a struct Change cfg80211 and mac80211 to use cfg80211_mgmt_tx_params struct to aggregate parameters for mgmt_tx functions. This makes the functions' signatures less clumsy and allows less painful parameters extension. Signed-off-by: Andrei Otcheretianski [fix all other drivers] Signed-off-by: Johannes Berg --- net/wireless/core.h | 5 ++--- net/wireless/mlme.c | 12 ++++-------- net/wireless/nl80211.c | 32 ++++++++++++++++---------------- net/wireless/rdev-ops.h | 12 ++++-------- net/wireless/trace.h | 15 +++++++-------- 5 files changed, 33 insertions(+), 43 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/core.h b/net/wireless/core.h index 2888867ee7c5..6716c5c3f748 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, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 31f541f7e4ea..52cca05044a8 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, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8c83fbb3824f..703155b1aa7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7428,10 +7428,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; @@ -7458,24 +7458,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 @@ -7487,10 +7487,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; @@ -7503,10 +7503,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, ¶ms, &cookie); if (err) goto free_msg; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 37ce9fdfe934..a6c03ab14a0d 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/trace.h b/net/wireless/trace.h index ba5f0d6614d5..f7aa7a72d9bc 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", -- cgit v1.2.3 From c56589ed1d25ae110a5b000bc1edea8f6861348f Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 21 Nov 2013 18:19:49 +0100 Subject: cfg80211: protect beacon changing functions with wdev-lock To avoid race conditions in functions which modify the beacon information, lock these using the wdev lock. This is especially required to avoid problems for csa handling functions which modify beacons but can not be called under rtnl lock. Reported-by: Johannes Berg Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 703155b1aa7a..398756c226c3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3218,6 +3218,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, ¶ms); if (!err) { wdev->preset_chandef = params.chandef; @@ -3226,6 +3227,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); @@ -3254,7 +3256,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) if (err) return err; - return rdev_change_beacon(rdev, dev, ¶ms); + wdev_lock(wdev); + err = rdev_change_beacon(rdev, dev, ¶ms); + wdev_unlock(wdev); + + return err; } static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) @@ -4460,7 +4466,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(¶ms, 0, sizeof(params)); /* default to not changing parameters */ @@ -4526,7 +4534,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, ¶ms); + wdev_lock(wdev); + err = rdev_change_bss(rdev, dev, ¶ms); + wdev_unlock(wdev); + + return err; } static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { @@ -5791,7 +5803,11 @@ skip_beacons: if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) params.block_tx = true; - return rdev_channel_switch(rdev, dev, ¶ms); + wdev_lock(wdev); + err = rdev_channel_switch(rdev, dev, ¶ms); + wdev_unlock(wdev); + + return err; } static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, -- cgit v1.2.3 From e487eaeb076a44c69dc61348cbc903151bb8fcbd Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 21 Nov 2013 18:19:51 +0100 Subject: cfg80211/mac80211/ath6kl: acquire wdev lock outside ch_switch_notify The channel switch notification should be sent under the wdev/sdata-lock, preferably in the same moment as the channel change happens, to avoid races by other callers (e.g. start/stop_ap). This also adds the previously missing sdata_lock protection in csa_finalize_work. Reported-by: Johannes Berg Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'net/wireless') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 398756c226c3..95882a788b5b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10821,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); -- cgit v1.2.3