summaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-01-25 11:17:34 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-25 11:17:34 -0800
commit4ba9920e5e9c0e16b5ed24292d45322907bb9035 (patch)
tree7d023baea59ed0886ded1f0b6d1c6385690b88f7 /net/wireless
parent82c477669a4665eb4e52030792051e0559ee2a36 (diff)
parent8b662fe70c68282f78482dc272df0c4f355e49f5 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) BPF debugger and asm tool by Daniel Borkmann. 2) Speed up create/bind in AF_PACKET, also from Daniel Borkmann. 3) Correct reciprocal_divide and update users, from Hannes Frederic Sowa and Daniel Borkmann. 4) Currently we only have a "set" operation for the hw timestamp socket ioctl, add a "get" operation to match. From Ben Hutchings. 5) Add better trace events for debugging driver datapath problems, also from Ben Hutchings. 6) Implement auto corking in TCP, from Eric Dumazet. Basically, if we have a small send and a previous packet is already in the qdisc or device queue, defer until TX completion or we get more data. 7) Allow userspace to manage ipv6 temporary addresses, from Jiri Pirko. 8) Add a qdisc bypass option for AF_PACKET sockets, from Daniel Borkmann. 9) Share IP header compression code between Bluetooth and IEEE802154 layers, from Jukka Rissanen. 10) Fix ipv6 router reachability probing, from Jiri Benc. 11) Allow packets to be captured on macvtap devices, from Vlad Yasevich. 12) Support tunneling in GRO layer, from Jerry Chu. 13) Allow bonding to be configured fully using netlink, from Scott Feldman. 14) Allow AF_PACKET users to obtain the VLAN TPID, just like they can already get the TCI. From Atzm Watanabe. 15) New "Heavy Hitter" qdisc, from Terry Lam. 16) Significantly improve the IPSEC support in pktgen, from Fan Du. 17) Allow ipv4 tunnels to cache routes, just like sockets. From Tom Herbert. 18) Add Proportional Integral Enhanced packet scheduler, from Vijay Subramanian. 19) Allow openvswitch to mmap'd netlink, from Thomas Graf. 20) Key TCP metrics blobs also by source address, not just destination address. From Christoph Paasch. 21) Support 10G in generic phylib. From Andy Fleming. 22) Try to short-circuit GRO flow compares using device provided RX hash, if provided. From Tom Herbert. The wireless and netfilter folks have been busy little bees too. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (2064 commits) net/cxgb4: Fix referencing freed adapter ipv6: reallocate addrconf router for ipv6 address when lo device up fib_frontend: fix possible NULL pointer dereference rtnetlink: remove IFLA_BOND_SLAVE definition rtnetlink: remove check for fill_slave_info in rtnl_have_link_slave_info qlcnic: update version to 5.3.55 qlcnic: Enhance logic to calculate msix vectors. qlcnic: Refactor interrupt coalescing code for all adapters. qlcnic: Update poll controller code path qlcnic: Interrupt code cleanup qlcnic: Enhance Tx timeout debugging. qlcnic: Use bool for rx_mac_learn. bonding: fix u64 division rtnetlink: add missing IFLA_BOND_AD_INFO_UNSPEC sfc: Use the correct maximum TX DMA ring size for SFC9100 Add Shradha Shah as the sfc driver maintainer. net/vxlan: Share RX skb de-marking and checksum checks with ovs tulip: cleanup by using ARRAY_SIZE() ip_tunnel: clear IPCB in ip_tunnel_xmit() in case dst_link_failure() is called net/cxgb4: Don't retrieve stats during recovery ...
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/ap.c1
-rw-r--r--net/wireless/chan.c203
-rw-r--r--net/wireless/core.c31
-rw-r--r--net/wireless/core.h24
-rw-r--r--net/wireless/genregdb.awk45
-rw-r--r--net/wireless/ibss.c6
-rw-r--r--net/wireless/mesh.c17
-rw-r--r--net/wireless/mlme.c20
-rw-r--r--net/wireless/nl80211.c766
-rw-r--r--net/wireless/nl80211.h2
-rw-r--r--net/wireless/rdev-ops.h27
-rw-r--r--net/wireless/reg.c831
-rw-r--r--net/wireless/reg.h4
-rw-r--r--net/wireless/scan.c23
-rw-r--r--net/wireless/sme.c15
-rw-r--r--net/wireless/trace.h55
-rw-r--r--net/wireless/util.c34
-rw-r--r--net/wireless/wext-compat.c6
18 files changed, 1412 insertions, 698 deletions
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index 324e8d851dc4..11ee4ed04f73 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -29,6 +29,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
wdev->beacon_interval = 0;
wdev->channel = NULL;
wdev->ssid_len = 0;
+ rdev_set_qos_map(rdev, dev, NULL);
}
return err;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 9b8cc877eb19..78559b5bbd1f 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -277,6 +277,32 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
width, dfs_state);
}
+static u32 cfg80211_get_start_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 start_freq;
+
+ if (bandwidth <= 20)
+ start_freq = center_freq;
+ else
+ start_freq = center_freq - bandwidth/2 + 10;
+
+ return start_freq;
+}
+
+static u32 cfg80211_get_end_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 end_freq;
+
+ if (bandwidth <= 20)
+ end_freq = center_freq;
+ else
+ end_freq = center_freq + bandwidth/2 - 10;
+
+ return end_freq;
+}
+
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
u32 center_freq,
u32 bandwidth)
@@ -284,13 +310,8 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
struct ieee80211_channel *c;
u32 freq, start_freq, end_freq;
- if (bandwidth <= 20) {
- start_freq = center_freq;
- end_freq = center_freq;
- } else {
- start_freq = center_freq - bandwidth/2 + 10;
- end_freq = center_freq + bandwidth/2 - 10;
- }
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
@@ -330,33 +351,159 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
-static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
- u32 center_freq, u32 bandwidth,
- u32 prohibited_flags)
+static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
{
struct ieee80211_channel *c;
u32 freq, start_freq, end_freq;
+ int count = 0;
- if (bandwidth <= 20) {
- start_freq = center_freq;
- end_freq = center_freq;
- } else {
- start_freq = center_freq - bandwidth/2 + 10;
- end_freq = center_freq + bandwidth/2 - 10;
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * Check all channels are DFS channels (DFS_USABLE or
+ * DFS_AVAILABLE). Return number of usable channels
+ * (require CAC). Allow DFS and non-DFS channel mix.
+ */
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_RADAR) {
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
+ return -EINVAL;
+
+ if (c->dfs_state == NL80211_DFS_USABLE)
+ count++;
+ }
}
+ return count;
+}
+
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r1, r2 = 0;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1,
+ width);
+
+ if (r1 < 0)
+ return false;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r2 = cfg80211_get_chans_dfs_usable(wiphy,
+ chandef->center_freq2,
+ width);
+ if (r2 < 0)
+ return false;
+ break;
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return (r1 + r2 > 0);
+}
+
+
+static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * If any channel in between is disabled or has not
+ * had gone through CAC return false
+ */
for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c)
return false;
- /* check for radar flags */
- if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return false;
+
+ if ((c->flags & IEEE80211_CHAN_RADAR) &&
(c->dfs_state != NL80211_DFS_AVAILABLE))
return false;
+ }
+
+ return true;
+}
+
+static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1,
+ width);
+
+ /* If any of channels unavailable for cf1 just return */
+ if (!r)
+ return r;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r = cfg80211_get_chans_dfs_available(wiphy,
+ chandef->center_freq2,
+ width);
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return r;
+}
- /* check for the other flags */
- if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
+
+static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || c->flags & prohibited_flags)
return false;
}
@@ -462,14 +609,19 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
bool res;
+ u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR;
trace_cfg80211_reg_can_beacon(wiphy, chandef);
- res = cfg80211_chandef_usable(wiphy, chandef,
- IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_PASSIVE_SCAN |
- IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_RADAR);
+ if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 &&
+ cfg80211_chandef_dfs_available(wiphy, chandef)) {
+ /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
+ prohibited_flags = IEEE80211_CHAN_DISABLED;
+ }
+
+ res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
trace_cfg80211_return_bool(res);
return res;
@@ -510,6 +662,7 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
: CHAN_MODE_EXCLUSIVE;
return;
}
+ break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (wdev->current_bss) {
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 52b865fb7351..d89dee2259b5 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -203,17 +203,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
rdev->opencount--;
- if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
- /*
- * If the scan request wasn't notified as done, set it
- * to aborted and leak it after a warning. The driver
- * should have notified us that it ended at the latest
- * during rdev_stop_p2p_device().
- */
- if (WARN_ON(!rdev->scan_req->notified))
- rdev->scan_req->aborted = true;
- ___cfg80211_scan_done(rdev, !rdev->scan_req->notified);
- }
+ WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
+ !rdev->scan_req->notified);
}
static int cfg80211_rfkill_set_block(void *data, bool blocked)
@@ -357,8 +348,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);
@@ -575,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) {
@@ -595,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);
@@ -765,13 +756,16 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
{
struct net_device *dev = wdev->netdev;
+ ASSERT_RTNL();
+
switch (wdev->iftype) {
case NL80211_IFTYPE_ADHOC:
cfg80211_leave_ibss(rdev, dev, true);
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
- __cfg80211_stop_sched_scan(rdev, false);
+ if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev)
+ __cfg80211_stop_sched_scan(rdev, false);
wdev_lock(wdev);
#ifdef CONFIG_CFG80211_WEXT
@@ -865,11 +859,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
case NETDEV_DOWN:
cfg80211_update_iface_num(rdev, wdev->iftype, -1);
- if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
- if (WARN_ON(!rdev->scan_req->notified))
- rdev->scan_req->aborted = true;
- ___cfg80211_scan_done(rdev, true);
- }
+ WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
+ !rdev->scan_req->notified);
if (WARN_ON(rdev->sched_scan_req &&
rdev->sched_scan_req->dev == wdev->netdev)) {
diff --git a/net/wireless/core.h b/net/wireless/core.h
index af10e59af2d8..37ec16d7bb1a 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -67,9 +67,7 @@ struct cfg80211_registered_device {
struct work_struct scan_done_wk;
struct work_struct sched_scan_results_wk;
-#ifdef CONFIG_NL80211_TESTMODE
- struct genl_info *testmode_info;
-#endif
+ struct genl_info *cur_cmd_info;
struct work_struct conn_work;
struct work_struct event_work;
@@ -317,9 +315,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,
@@ -364,7 +361,7 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct key_params *params, int key_idx,
bool pairwise, const u8 *mac_addr);
void __cfg80211_scan_done(struct work_struct *wk);
-void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
+void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev);
void __cfg80211_sched_scan_results(struct work_struct *wk);
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
bool driver_initiated);
@@ -382,6 +379,19 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
enum cfg80211_chan_mode chanmode,
u8 radar_detect);
+/**
+ * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ *
+ * Checks if chandef is usable and we can/need start CAC on such channel.
+ *
+ * Return: Return true if all channels available and at least
+ * one channel require CAC (NL80211_DFS_USABLE)
+ */
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef);
+
void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state);
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
index 42ed274e81f4..9a8217d2a908 100644
--- a/net/wireless/genregdb.awk
+++ b/net/wireless/genregdb.awk
@@ -33,15 +33,7 @@ BEGIN {
regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
}
-/^[ \t]*#/ {
- # Ignore
-}
-
-!active && /^[ \t]*$/ {
- # Ignore
-}
-
-!active && /country/ {
+function parse_country_head() {
country=$2
sub(/:/, "", country)
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
@@ -57,7 +49,8 @@ BEGIN {
regdb = regdb "\t&regdom_" country ",\n"
}
-active && /^[ \t]*\(/ {
+function parse_reg_rule()
+{
start = $1
sub(/\(/, "", start)
end = $3
@@ -107,17 +100,21 @@ active && /^[ \t]*\(/ {
} else if (flagarray[arg] == "PTMP-ONLY") {
flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
} else if (flagarray[arg] == "PASSIVE-SCAN") {
- flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
} else if (flagarray[arg] == "NO-IBSS") {
- flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+ } else if (flagarray[arg] == "NO-IR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
}
+
}
flags = flags "0"
printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
rules++
}
-active && /^[ \t]*$/ {
+function print_tail_country()
+{
active = 0
printf "\t},\n"
printf "\t.n_reg_rules = %d\n", rules
@@ -125,7 +122,29 @@ active && /^[ \t]*$/ {
rules = 0;
}
+/^[ \t]*#/ {
+ # Ignore
+}
+
+!active && /^[ \t]*$/ {
+ # Ignore
+}
+
+!active && /country/ {
+ parse_country_head()
+}
+
+active && /^[ \t]*\(/ {
+ parse_reg_rule()
+}
+
+active && /^[ \t]*$/ {
+ print_tail_country()
+}
+
END {
+ if (active)
+ print_tail_country()
print regdb "};"
print ""
print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 89737ee2669a..f911c5f9f903 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -183,6 +183,8 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
kfree(wdev->connect_keys);
wdev->connect_keys = NULL;
+ rdev_set_qos_map(rdev, dev, NULL);
+
/*
* Delete all the keys ... pairwise keys can't really
* exist any more anyway, but default keys might.
@@ -274,7 +276,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;
@@ -346,7 +348,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..885862447b63 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -99,6 +99,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
const struct mesh_config *conf)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ u8 radar_detect_width = 0;
int err;
BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN);
@@ -141,8 +142,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;
@@ -178,8 +178,16 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
return -EINVAL;
- err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan,
- CHAN_MODE_SHARED);
+ err = cfg80211_chandef_dfs_required(wdev->wiphy, &setup->chandef);
+ if (err < 0)
+ return err;
+ if (err)
+ radar_detect_width = BIT(setup->chandef.width);
+
+ err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+ setup->chandef.chan,
+ CHAN_MODE_SHARED,
+ radar_detect_width);
if (err)
return err;
@@ -269,6 +277,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
if (!err) {
wdev->mesh_id_len = 0;
wdev->channel = NULL;
+ rdev_set_qos_map(rdev, dev, NULL);
}
return err;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 6a6b1c8e907d..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,
@@ -763,12 +759,12 @@ void cfg80211_radar_event(struct wiphy *wiphy,
EXPORT_SYMBOL(cfg80211_radar_event);
void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event, gfp_t gfp)
{
struct wireless_dev *wdev = netdev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- struct cfg80211_chan_def chandef;
unsigned long timeout;
trace_cfg80211_cac_event(netdev, event);
@@ -779,14 +775,12 @@ void cfg80211_cac_event(struct net_device *netdev,
if (WARN_ON(!wdev->channel))
return;
- cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
-
switch (event) {
case NL80211_RADAR_CAC_FINISHED:
timeout = wdev->cac_start_time +
msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
WARN_ON(!time_after_eq(jiffies, timeout));
- cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
break;
case NL80211_RADAR_CAC_ABORTED:
break;
@@ -796,6 +790,6 @@ void cfg80211_cac_event(struct net_device *netdev,
}
wdev->cac_started = false;
- nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
+ nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
}
EXPORT_SYMBOL(cfg80211_cac_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 138dc3bb8b67..7a742594916e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -53,6 +53,7 @@ enum nl80211_multicast_groups {
NL80211_MCGRP_SCAN,
NL80211_MCGRP_REGULATORY,
NL80211_MCGRP_MLME,
+ NL80211_MCGRP_VENDOR,
NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
};
@@ -61,6 +62,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
[NL80211_MCGRP_SCAN] = { .name = "scan", },
[NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
[NL80211_MCGRP_MLME] = { .name = "mlme", },
+ [NL80211_MCGRP_VENDOR] = { .name = "vendor", },
#ifdef CONFIG_NL80211_TESTMODE
[NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
#endif
@@ -163,7 +165,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
if (attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
- netdev = dev_get_by_index(netns, ifindex);
+ netdev = __dev_get_by_index(netns, ifindex);
if (netdev) {
if (netdev->ieee80211_ptr)
tmp = wiphy_to_dev(
@@ -171,8 +173,6 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
else
tmp = NULL;
- dev_put(netdev);
-
/* not wireless device -- return error */
if (!tmp)
return ERR_PTR(-EINVAL);
@@ -376,6 +376,12 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
+ [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 },
+ [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
+ [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
+ [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
+ [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
+ .len = IEEE80211_QOS_MAP_LEN_MAX },
};
/* policy for the key attributes */
@@ -564,12 +570,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
- goto nla_put_failure;
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
+ goto nla_put_failure;
+ if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
+ goto nla_put_failure;
+ }
if (chan->flags & IEEE80211_CHAN_RADAR) {
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
goto nla_put_failure;
@@ -1247,10 +1253,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;
@@ -1454,6 +1456,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
CMD(channel_switch, CHANNEL_SWITCH);
}
+ CMD(set_qos_map, SET_QOS_MAP);
#ifdef CONFIG_NL80211_TESTMODE
CMD(testmode_cmd, TESTMODE);
@@ -1579,6 +1582,46 @@ 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;
+ state->split_start++;
+ break;
+ case 11:
+ if (dev->wiphy.n_vendor_commands) {
+ const struct nl80211_vendor_cmd_info *info;
+ struct nlattr *nested;
+
+ nested = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = 0; i < dev->wiphy.n_vendor_commands; i++) {
+ info = &dev->wiphy.vendor_commands[i].info;
+ if (nla_put(msg, i + 1, sizeof(*info), info))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nested);
+ }
+
+ if (dev->wiphy.n_vendor_events) {
+ const struct nl80211_vendor_cmd_info *info;
+ struct nlattr *nested;
+
+ nested = nla_nest_start(msg,
+ NL80211_ATTR_VENDOR_EVENTS);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = 0; i < dev->wiphy.n_vendor_events; i++) {
+ info = &dev->wiphy.vendor_events[i];
+ if (nla_put(msg, i + 1, sizeof(*info), info))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nested);
+ }
+
/* done */
state->split_start = 0;
break;
@@ -1611,7 +1654,7 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
struct cfg80211_registered_device *rdev;
int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
- netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
+ netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
if (!netdev)
return -ENODEV;
if (netdev->ieee80211_ptr) {
@@ -1619,7 +1662,6 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
netdev->ieee80211_ptr->wiphy);
state->filter_wiphy = rdev->wiphy_idx;
}
- dev_put(netdev);
}
return 0;
@@ -1942,7 +1984,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
- netdev = dev_get_by_index(genl_info_net(info), ifindex);
+ netdev = __dev_get_by_index(genl_info_net(info), ifindex);
if (netdev && netdev->ieee80211_ptr)
rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
else
@@ -1970,32 +2012,24 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
if (result)
- goto bad_res;
+ return result;
if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
struct ieee80211_txq_params txq_params;
struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
- if (!rdev->ops->set_txq_params) {
- result = -EOPNOTSUPP;
- goto bad_res;
- }
+ if (!rdev->ops->set_txq_params)
+ return -EOPNOTSUPP;
- if (!netdev) {
- result = -EINVAL;
- goto bad_res;
- }
+ if (!netdev)
+ return -EINVAL;
if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
- netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
- result = -EINVAL;
- goto bad_res;
- }
+ netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
- if (!netif_running(netdev)) {
- result = -ENETDOWN;
- goto bad_res;
- }
+ if (!netif_running(netdev))
+ return -ENETDOWN;
nla_for_each_nested(nl_txq_params,
info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
@@ -2006,12 +2040,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
txq_params_policy);
result = parse_txq_params(tb, &txq_params);
if (result)
- goto bad_res;
+ return result;
result = rdev_set_txq_params(rdev, netdev,
&txq_params);
if (result)
- goto bad_res;
+ return result;
}
}
@@ -2020,7 +2054,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
info);
if (result)
- goto bad_res;
+ return result;
}
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
@@ -2031,19 +2065,15 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
txp_wdev = NULL;
- if (!rdev->ops->set_tx_power) {
- result = -EOPNOTSUPP;
- goto bad_res;
- }
+ if (!rdev->ops->set_tx_power)
+ return -EOPNOTSUPP;
idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
type = nla_get_u32(info->attrs[idx]);
if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
- (type != NL80211_TX_POWER_AUTOMATIC)) {
- result = -EINVAL;
- goto bad_res;
- }
+ (type != NL80211_TX_POWER_AUTOMATIC))
+ return -EINVAL;
if (type != NL80211_TX_POWER_AUTOMATIC) {
idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
@@ -2052,7 +2082,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
if (result)
- goto bad_res;
+ return result;
}
if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
@@ -2060,10 +2090,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
u32 tx_ant, rx_ant;
if ((!rdev->wiphy.available_antennas_tx &&
!rdev->wiphy.available_antennas_rx) ||
- !rdev->ops->set_antenna) {
- result = -EOPNOTSUPP;
- goto bad_res;
- }
+ !rdev->ops->set_antenna)
+ return -EOPNOTSUPP;
tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
@@ -2071,17 +2099,15 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
/* reject antenna configurations which don't match the
* available antenna masks, except for the "all" mask */
if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
- (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
- result = -EINVAL;
- goto bad_res;
- }
+ (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx)))
+ return -EINVAL;
tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
result = rdev_set_antenna(rdev, tx_ant, rx_ant);
if (result)
- goto bad_res;
+ return result;
}
changed = 0;
@@ -2089,30 +2115,27 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
retry_short = nla_get_u8(
info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
- if (retry_short == 0) {
- result = -EINVAL;
- goto bad_res;
- }
+ if (retry_short == 0)
+ return -EINVAL;
+
changed |= WIPHY_PARAM_RETRY_SHORT;
}
if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
retry_long = nla_get_u8(
info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
- if (retry_long == 0) {
- result = -EINVAL;
- goto bad_res;
- }
+ if (retry_long == 0)
+ return -EINVAL;
+
changed |= WIPHY_PARAM_RETRY_LONG;
}
if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
frag_threshold = nla_get_u32(
info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
- if (frag_threshold < 256) {
- result = -EINVAL;
- goto bad_res;
- }
+ if (frag_threshold < 256)
+ return -EINVAL;
+
if (frag_threshold != (u32) -1) {
/*
* Fragments (apart from the last one) are required to
@@ -2142,10 +2165,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
u32 old_frag_threshold, old_rts_threshold;
u8 old_coverage_class;
- if (!rdev->ops->set_wiphy_params) {
- result = -EOPNOTSUPP;
- goto bad_res;
- }
+ if (!rdev->ops->set_wiphy_params)
+ return -EOPNOTSUPP;
old_retry_short = rdev->wiphy.retry_short;
old_retry_long = rdev->wiphy.retry_long;
@@ -2173,11 +2194,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.coverage_class = old_coverage_class;
}
}
-
- bad_res:
- if (netdev)
- dev_put(netdev);
- return result;
+ return 0;
}
static inline u64 wdev_id(struct wireless_dev *wdev)
@@ -2187,7 +2204,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
}
static int nl80211_send_chandef(struct sk_buff *msg,
- struct cfg80211_chan_def *chandef)
+ const struct cfg80211_chan_def *chandef)
{
WARN_ON(!cfg80211_chandef_valid(chandef));
@@ -3236,6 +3253,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(params.acl);
}
+ wdev_lock(wdev);
err = rdev_start_ap(rdev, dev, &params);
if (!err) {
wdev->preset_chandef = params.chandef;
@@ -3244,6 +3262,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
}
+ wdev_unlock(wdev);
kfree(params.acl);
@@ -3272,7 +3291,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
- return rdev_change_beacon(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_change_beacon(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
@@ -4144,6 +4167,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.vht_capa =
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
+ params.opmode_notif_used = true;
+ params.opmode_notif =
+ nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
+ }
+
if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
@@ -4478,7 +4507,9 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
struct bss_parameters params;
+ int err;
memset(&params, 0, sizeof(params));
/* default to not changing parameters */
@@ -4544,7 +4575,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
- return rdev_change_bss(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_change_bss(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
@@ -5098,7 +5133,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
char *alpha2 = NULL;
int rem_reg_rules = 0, r = 0;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
- u8 dfs_region = 0;
+ enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
struct ieee80211_regdomain *rd = NULL;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -5119,6 +5154,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);
@@ -5219,12 +5257,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
goto unlock;
}
} else {
- enum ieee80211_band band;
- n_channels = 0;
-
- for (band = 0; band < IEEE80211_NUM_BANDS; band++)
- if (wiphy->bands[band])
- n_channels += wiphy->bands[band]->n_channels;
+ n_channels = ieee80211_get_num_supported_channels(wiphy);
}
if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
@@ -5365,10 +5398,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;
}
@@ -5434,11 +5465,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (!n_channels)
return -EINVAL;
} else {
- n_channels = 0;
-
- for (band = 0; band < IEEE80211_NUM_BANDS; band++)
- if (wiphy->bands[band])
- n_channels += wiphy->bands[band]->n_channels;
+ n_channels = ieee80211_get_num_supported_channels(wiphy);
}
if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
@@ -5608,10 +5635,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;
}
@@ -5655,8 +5680,13 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_chan_def chandef;
+ enum nl80211_dfs_regions dfs_region;
int err;
+ dfs_region = reg_get_dfs_region(wdev->wiphy);
+ if (dfs_region == NL80211_DFS_UNSET)
+ return -EINVAL;
+
err = nl80211_parse_chandef(rdev, info, &chandef);
if (err)
return err;
@@ -5674,7 +5704,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)
@@ -5814,7 +5844,11 @@ skip_beacons:
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
params.block_tx = true;
- return rdev_channel_switch(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_channel_switch(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
@@ -6677,6 +6711,101 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static struct sk_buff *
+__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
+ int approxlen, u32 portid, u32 seq,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ const struct nl80211_vendor_cmd_info *info,
+ gfp_t gfp)
+{
+ struct sk_buff *skb;
+ void *hdr;
+ struct nlattr *data;
+
+ skb = nlmsg_new(approxlen + 100, gfp);
+ if (!skb)
+ return NULL;
+
+ hdr = nl80211hdr_put(skb, portid, seq, 0, cmd);
+ if (!hdr) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+ goto nla_put_failure;
+
+ if (info) {
+ if (nla_put_u32(skb, NL80211_ATTR_VENDOR_ID,
+ info->vendor_id))
+ goto nla_put_failure;
+ if (nla_put_u32(skb, NL80211_ATTR_VENDOR_SUBCMD,
+ info->subcmd))
+ goto nla_put_failure;
+ }
+
+ data = nla_nest_start(skb, attr);
+
+ ((void **)skb->cb)[0] = rdev;
+ ((void **)skb->cb)[1] = hdr;
+ ((void **)skb->cb)[2] = data;
+
+ return skb;
+
+ nla_put_failure:
+ kfree_skb(skb);
+ return NULL;
+}
+
+struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int vendor_event_idx,
+ int approxlen, gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ const struct nl80211_vendor_cmd_info *info;
+
+ switch (cmd) {
+ case NL80211_CMD_TESTMODE:
+ if (WARN_ON(vendor_event_idx != -1))
+ return NULL;
+ info = NULL;
+ break;
+ case NL80211_CMD_VENDOR:
+ if (WARN_ON(vendor_event_idx < 0 ||
+ vendor_event_idx >= wiphy->n_vendor_events))
+ return NULL;
+ info = &wiphy->vendor_events[vendor_event_idx];
+ break;
+ default:
+ WARN_ON(1);
+ return NULL;
+ }
+
+ return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
+ cmd, attr, info, gfp);
+}
+EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
+
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
+ void *hdr = ((void **)skb->cb)[1];
+ struct nlattr *data = ((void **)skb->cb)[2];
+ enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE;
+
+ nla_nest_end(skb, data);
+ genlmsg_end(skb, hdr);
+
+ if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
+ mcgrp = NL80211_MCGRP_VENDOR;
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
+ mcgrp, gfp);
+}
+EXPORT_SYMBOL(__cfg80211_send_event_skb);
#ifdef CONFIG_NL80211_TESTMODE
static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
@@ -6701,11 +6830,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NL80211_ATTR_TESTDATA])
return -EINVAL;
- rdev->testmode_info = info;
+ rdev->cur_cmd_info = info;
err = rdev_testmode_cmd(rdev, wdev,
nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
- rdev->testmode_info = NULL;
+ rdev->cur_cmd_info = NULL;
return err;
}
@@ -6804,93 +6933,6 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
rtnl_unlock();
return err;
}
-
-static struct sk_buff *
-__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
- int approxlen, u32 portid, u32 seq, gfp_t gfp)
-{
- struct sk_buff *skb;
- void *hdr;
- struct nlattr *data;
-
- skb = nlmsg_new(approxlen + 100, gfp);
- if (!skb)
- return NULL;
-
- hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE);
- if (!hdr) {
- kfree_skb(skb);
- return NULL;
- }
-
- if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
- goto nla_put_failure;
- data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
-
- ((void **)skb->cb)[0] = rdev;
- ((void **)skb->cb)[1] = hdr;
- ((void **)skb->cb)[2] = data;
-
- return skb;
-
- nla_put_failure:
- kfree_skb(skb);
- return NULL;
-}
-
-struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
- int approxlen)
-{
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- if (WARN_ON(!rdev->testmode_info))
- return NULL;
-
- return __cfg80211_testmode_alloc_skb(rdev, approxlen,
- rdev->testmode_info->snd_portid,
- rdev->testmode_info->snd_seq,
- GFP_KERNEL);
-}
-EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
-
-int cfg80211_testmode_reply(struct sk_buff *skb)
-{
- struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
- void *hdr = ((void **)skb->cb)[1];
- struct nlattr *data = ((void **)skb->cb)[2];
-
- if (WARN_ON(!rdev->testmode_info)) {
- kfree_skb(skb);
- return -EINVAL;
- }
-
- nla_nest_end(skb, data);
- genlmsg_end(skb, hdr);
- return genlmsg_reply(skb, rdev->testmode_info);
-}
-EXPORT_SYMBOL(cfg80211_testmode_reply);
-
-struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
- int approxlen, gfp_t gfp)
-{
- struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
- return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
-}
-EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
-
-void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
-{
- struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
- void *hdr = ((void **)skb->cb)[1];
- struct nlattr *data = ((void **)skb->cb)[2];
-
- nla_nest_end(skb, data);
- genlmsg_end(skb, hdr);
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
- NL80211_MCGRP_TESTMODE, gfp);
-}
-EXPORT_SYMBOL(cfg80211_testmode_event);
#endif
static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
@@ -7312,11 +7354,72 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
return true;
}
+static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
+{
+ u16 mcs_mask = 0;
+
+ switch (vht_mcs_map) {
+ case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ mcs_mask = 0x00FF;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ mcs_mask = 0x01FF;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ mcs_mask = 0x03FF;
+ break;
+ default:
+ break;
+ }
+
+ return mcs_mask;
+}
+
+static void vht_build_mcs_mask(u16 vht_mcs_map,
+ u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
+{
+ u8 nss;
+
+ for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
+ vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
+ vht_mcs_map >>= 2;
+ }
+}
+
+static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
+ struct nl80211_txrate_vht *txrate,
+ u16 mcs[NL80211_VHT_NSS_MAX])
+{
+ u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
+ u8 i;
+
+ if (!sband->vht_cap.vht_supported)
+ return false;
+
+ memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
+
+ /* Build vht_mcs_mask from VHT capabilities */
+ vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
+
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+ if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+ mcs[i] = txrate->mcs[i];
+ else
+ return false;
+ }
+
+ return true;
+}
+
static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
[NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
.len = NL80211_MAX_SUPP_RATES },
- [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
- .len = NL80211_MAX_SUPP_HT_RATES },
+ [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_HT_RATES },
+ [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
};
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -7329,9 +7432,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
struct net_device *dev = info->user_ptr[1];
struct nlattr *tx_rates;
struct ieee80211_supported_band *sband;
-
- if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
- return -EINVAL;
+ u16 vht_tx_mcs_map;
if (!rdev->ops->set_bitrate_mask)
return -EOPNOTSUPP;
@@ -7340,17 +7441,26 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
/* Default to all rates enabled */
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
sband = rdev->wiphy.bands[i];
- mask.control[i].legacy =
- sband ? (1 << sband->n_bitrates) - 1 : 0;
- if (sband)
- memcpy(mask.control[i].mcs,
- sband->ht_cap.mcs.rx_mask,
- sizeof(mask.control[i].mcs));
- else
- memset(mask.control[i].mcs, 0,
- sizeof(mask.control[i].mcs));
+
+ if (!sband)
+ continue;
+
+ mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
+ memcpy(mask.control[i].ht_mcs,
+ sband->ht_cap.mcs.rx_mask,
+ sizeof(mask.control[i].ht_mcs));
+
+ if (!sband->vht_cap.vht_supported)
+ continue;
+
+ vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
}
+ /* if no rates are given set it back to the defaults */
+ if (!info->attrs[NL80211_ATTR_TX_RATES])
+ goto out;
+
/*
* The nested attribute uses enum nl80211_band as the index. This maps
* directly to the enum ieee80211_band values used in cfg80211.
@@ -7375,31 +7485,44 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
nla_len(tb[NL80211_TXRATE_LEGACY]))
return -EINVAL;
}
- if (tb[NL80211_TXRATE_MCS]) {
+ if (tb[NL80211_TXRATE_HT]) {
if (!ht_rateset_to_mask(
sband,
- nla_data(tb[NL80211_TXRATE_MCS]),
- nla_len(tb[NL80211_TXRATE_MCS]),
- mask.control[band].mcs))
+ nla_data(tb[NL80211_TXRATE_HT]),
+ nla_len(tb[NL80211_TXRATE_HT]),
+ mask.control[band].ht_mcs))
+ return -EINVAL;
+ }
+ if (tb[NL80211_TXRATE_VHT]) {
+ if (!vht_set_mcs_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_VHT]),
+ mask.control[band].vht_mcs))
return -EINVAL;
}
if (mask.control[band].legacy == 0) {
- /* don't allow empty legacy rates if HT
- * is not even supported. */
- if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
+ /* don't allow empty legacy rates if HT or VHT
+ * are not even supported.
+ */
+ if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
+ rdev->wiphy.bands[band]->vht_cap.vht_supported))
return -EINVAL;
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
- if (mask.control[band].mcs[i])
- break;
+ if (mask.control[band].ht_mcs[i])
+ goto out;
+
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+ if (mask.control[band].vht_mcs[i])
+ goto out;
/* legacy and mcs rates may not be both empty */
- if (i == IEEE80211_HT_MCS_MASK_LEN)
- return -EINVAL;
+ return -EINVAL;
}
}
+out:
return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
}
@@ -7447,10 +7570,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;
@@ -7477,24 +7600,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
@@ -7506,10 +7629,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;
@@ -7522,10 +7645,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
}
- err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
- nla_data(info->attrs[NL80211_ATTR_FRAME]),
- nla_len(info->attrs[NL80211_ATTR_FRAME]),
- no_cck, dont_wait_for_ack, &cookie);
+ params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+ params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+ params.chan = chandef.chan;
+ err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
if (err)
goto free_msg;
@@ -8859,6 +8982,162 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
return 0;
}
+static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev =
+ __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+ int i, err;
+ u32 vid, subcmd;
+
+ if (!rdev->wiphy.vendor_commands)
+ return -EOPNOTSUPP;
+
+ if (IS_ERR(wdev)) {
+ err = PTR_ERR(wdev);
+ if (err != -EINVAL)
+ return err;
+ wdev = NULL;
+ } else if (wdev->wiphy != &rdev->wiphy) {
+ return -EINVAL;
+ }
+
+ if (!info->attrs[NL80211_ATTR_VENDOR_ID] ||
+ !info->attrs[NL80211_ATTR_VENDOR_SUBCMD])
+ return -EINVAL;
+
+ vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]);
+ subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]);
+ for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
+ const struct wiphy_vendor_command *vcmd;
+ void *data = NULL;
+ int len = 0;
+
+ vcmd = &rdev->wiphy.vendor_commands[i];
+
+ if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
+ continue;
+
+ if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_NETDEV)) {
+ if (!wdev)
+ return -EINVAL;
+ if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
+ !wdev->netdev)
+ return -EINVAL;
+
+ if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
+ if (wdev->netdev &&
+ !netif_running(wdev->netdev))
+ return -ENETDOWN;
+ if (!wdev->netdev && !wdev->p2p_started)
+ return -ENETDOWN;
+ }
+ } else {
+ wdev = NULL;
+ }
+
+ if (info->attrs[NL80211_ATTR_VENDOR_DATA]) {
+ data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+ len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+ }
+
+ rdev->cur_cmd_info = info;
+ err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev,
+ data, len);
+ rdev->cur_cmd_info = NULL;
+ return err;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int approxlen)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ if (WARN_ON(!rdev->cur_cmd_info))
+ return NULL;
+
+ return __cfg80211_alloc_vendor_skb(rdev, approxlen,
+ rdev->cur_cmd_info->snd_portid,
+ rdev->cur_cmd_info->snd_seq,
+ cmd, attr, NULL, GFP_KERNEL);
+}
+EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);
+
+int cfg80211_vendor_cmd_reply(struct sk_buff *skb)
+{
+ struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
+ void *hdr = ((void **)skb->cb)[1];
+ struct nlattr *data = ((void **)skb->cb)[2];
+
+ if (WARN_ON(!rdev->cur_cmd_info)) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ nla_nest_end(skb, data);
+ genlmsg_end(skb, hdr);
+ return genlmsg_reply(skb, rdev->cur_cmd_info);
+}
+EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply);
+
+
+static int nl80211_set_qos_map(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_qos_map *qos_map = NULL;
+ struct net_device *dev = info->user_ptr[1];
+ u8 *pos, len, num_des, des_len, des;
+ int ret;
+
+ if (!rdev->ops->set_qos_map)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_QOS_MAP]) {
+ pos = nla_data(info->attrs[NL80211_ATTR_QOS_MAP]);
+ len = nla_len(info->attrs[NL80211_ATTR_QOS_MAP]);
+
+ if (len % 2 || len < IEEE80211_QOS_MAP_LEN_MIN ||
+ len > IEEE80211_QOS_MAP_LEN_MAX)
+ return -EINVAL;
+
+ qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL);
+ if (!qos_map)
+ return -ENOMEM;
+
+ num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1;
+ if (num_des) {
+ des_len = num_des *
+ sizeof(struct cfg80211_dscp_exception);
+ memcpy(qos_map->dscp_exception, pos, des_len);
+ qos_map->num_des = num_des;
+ for (des = 0; des < num_des; des++) {
+ if (qos_map->dscp_exception[des].up > 7) {
+ kfree(qos_map);
+ return -EINVAL;
+ }
+ }
+ pos += des_len;
+ }
+ memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN);
+ }
+
+ wdev_lock(dev->ieee80211_ptr);
+ ret = nl80211_key_allowed(dev->ieee80211_ptr);
+ if (!ret)
+ ret = rdev_set_qos_map(rdev, dev, qos_map);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ kfree(qos_map);
+ return ret;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -9583,6 +9862,22 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_VENDOR,
+ .doit = nl80211_vendor_cmd,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_QOS_MAP,
+ .doit = nl80211_set_qos_map,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
/* notification functions */
@@ -10810,21 +11105,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);
@@ -10883,7 +11175,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);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 37ce9fdfe934..c8e225947adb 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;
}
@@ -936,4 +932,19 @@ static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_qos_map *qos_map)
+{
+ int ret = -EOPNOTSUPP;
+
+ if (rdev->ops->set_qos_map) {
+ trace_rdev_set_qos_map(&rdev->wiphy, dev, qos_map);
+ ret = rdev->ops->set_qos_map(&rdev->wiphy, dev, qos_map);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ }
+
+ return ret;
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 7da67fd0b418..9b897fca7487 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -120,6 +120,48 @@ 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";
+}
+
+enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
+{
+ const struct ieee80211_regdomain *regd = NULL;
+ const struct ieee80211_regdomain *wiphy_regd = NULL;
+
+ regd = get_cfg80211_regdom();
+ if (!wiphy)
+ goto out;
+
+ wiphy_regd = get_wiphy_regdom(wiphy);
+ if (!wiphy_regd)
+ goto out;
+
+ if (wiphy_regd->dfs_region == regd->dfs_region)
+ goto out;
+
+ REG_DBG_PRINT("%s: device specific dfs_region "
+ "(%s) disagrees with cfg80211's "
+ "central dfs_region (%s)\n",
+ dev_name(&wiphy->dev),
+ reg_dfs_region_str(wiphy_regd->dfs_region),
+ reg_dfs_region_str(regd->dfs_region));
+
+out:
+ return regd->dfs_region;
+}
+
static void rcu_free_regdom(const struct ieee80211_regdomain *r)
{
if (!r)
@@ -163,35 +205,29 @@ static const struct ieee80211_regdomain world_regdom = {
REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
/* IEEE 802.11b/g, channels 12..13. */
REG_RULE(2467-10, 2472+10, 40, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11 channel 14 - Only JP enables
* this and for 802.11b only */
REG_RULE(2484-10, 2484+10, 20, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_NO_OFDM),
/* IEEE 802.11a, channel 36..48 */
REG_RULE(5180-10, 5240+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11a, channel 52..64 - DFS required */
REG_RULE(5260-10, 5320+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 100..144 - DFS required */
REG_RULE(5500-10, 5720+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 149..165 */
REG_RULE(5745-10, 5825+10, 80, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11ad (60gHz), channels 1..3 */
REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
@@ -208,11 +244,26 @@ static char user_alpha2[2];
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
+static void reg_kfree_last_request(void)
+{
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+
+ if (lr != &core_request_world && lr)
+ kfree_rcu(lr, rcu_head);
+}
+
+static void reg_update_last_request(struct regulatory_request *request)
+{
+ reg_kfree_last_request();
+ rcu_assign_pointer(last_request, request);
+}
+
static void reset_regdomains(bool full_reset,
const struct ieee80211_regdomain *new_regdom)
{
const struct ieee80211_regdomain *r;
- struct regulatory_request *lr;
ASSERT_RTNL();
@@ -235,10 +286,7 @@ static void reset_regdomains(bool full_reset,
if (!full_reset)
return;
- lr = get_last_request();
- if (lr != &core_request_world && lr)
- kfree_rcu(lr, rcu_head);
- rcu_assign_pointer(last_request, &core_request_world);
+ reg_update_last_request(&core_request_world);
}
/*
@@ -456,7 +504,15 @@ static int call_crda(const char *alpha2)
return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
}
-static bool reg_is_valid_request(const char *alpha2)
+static enum reg_request_treatment
+reg_call_crda(struct regulatory_request *request)
+{
+ if (call_crda(request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_OK;
+}
+
+bool reg_is_valid_request(const char *alpha2)
{
struct regulatory_request *lr = get_last_request();
@@ -557,6 +613,20 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
}
/*
+ * Later on we can perhaps use the more restrictive DFS
+ * region but we don't have information for that yet so
+ * for now simply disallow conflicts.
+ */
+static enum nl80211_dfs_regions
+reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
+ const enum nl80211_dfs_regions dfs_region2)
+{
+ if (dfs_region1 != dfs_region2)
+ return NL80211_DFS_UNSET;
+ return dfs_region1;
+}
+
+/*
* Helper for regdom_intersect(), this does the real
* mathematical intersection fun
*/
@@ -687,6 +757,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
rd->n_reg_rules = num_rules;
rd->alpha2[0] = '9';
rd->alpha2[1] = '8';
+ rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region,
+ rd2->dfs_region);
return rd;
}
@@ -698,10 +770,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
static u32 map_regdom_flags(u32 rd_flags)
{
u32 channel_flags = 0;
- if (rd_flags & NL80211_RRF_PASSIVE_SCAN)
- channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN;
- if (rd_flags & NL80211_RRF_NO_IBSS)
- channel_flags |= IEEE80211_CHAN_NO_IBSS;
+ if (rd_flags & NL80211_RRF_NO_IR_ALL)
+ channel_flags |= IEEE80211_CHAN_NO_IR;
if (rd_flags & NL80211_RRF_DFS)
channel_flags |= IEEE80211_CHAN_RADAR;
if (rd_flags & NL80211_RRF_NO_OFDM)
@@ -854,8 +924,18 @@ static void handle_channel(struct wiphy *wiphy,
PTR_ERR(reg_rule) == -ERANGE)
return;
- REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq);
- chan->flags |= IEEE80211_CHAN_DISABLED;
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ request_wiphy && request_wiphy == wiphy &&
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
+ REG_DBG_PRINT("Disabling freq %d MHz for good\n",
+ chan->center_freq);
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
+ } else {
+ REG_DBG_PRINT("Disabling freq %d MHz\n",
+ chan->center_freq);
+ chan->flags |= IEEE80211_CHAN_DISABLED;
+ }
return;
}
@@ -873,7 +953,7 @@ static void handle_channel(struct wiphy *wiphy,
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
- request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
/*
* This guarantees the driver's requested regulatory domain
* will always be used as a base for further regulatory
@@ -899,13 +979,11 @@ static void handle_channel(struct wiphy *wiphy,
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
if (chan->orig_mpwr) {
/*
- * Devices that have their own custom regulatory domain
- * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
- * passed country IE power settings.
+ * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
+ * will always follow the passed country IE power settings.
*/
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER)
chan->max_power = chan->max_reg_power;
else
chan->max_power = min(chan->orig_mpwr,
@@ -975,8 +1053,8 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
{
- if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
- !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
+ if (wiphy->regulatory_flags & REGULATORY_STRICT_REG &&
+ !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG))
return true;
return false;
}
@@ -994,7 +1072,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
}
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
REG_DBG_PRINT("Ignoring regulatory request set by %s "
"since the driver uses its own custom "
"regulatory domain\n",
@@ -1032,7 +1110,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy)
return true;
if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
return true;
return false;
@@ -1060,19 +1138,14 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
if (!reg_is_world_roaming(wiphy))
return;
- if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS)
+ if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS)
return;
chan_before.center_freq = chan->center_freq;
chan_before.flags = chan->flags;
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
- chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
- channel_changed = true;
- }
-
- if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
- chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ chan->flags &= ~IEEE80211_CHAN_NO_IR;
channel_changed = true;
}
@@ -1205,14 +1278,30 @@ static void reg_process_ht_flags(struct wiphy *wiphy)
reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
}
+static void reg_call_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ if (wiphy->reg_notifier)
+ wiphy->reg_notifier(wiphy, request);
+}
+
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
enum ieee80211_band band;
struct regulatory_request *lr = get_last_request();
- if (ignore_reg_update(wiphy, initiator))
+ if (ignore_reg_update(wiphy, initiator)) {
+ /*
+ * Regulatory updates set by CORE are ignored for custom
+ * regulatory cards. Let us notify the changes to the driver,
+ * as some drivers used this to restore its orig_* reg domain.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_CORE &&
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
+ reg_call_notifier(wiphy, lr);
return;
+ }
lr->dfs_region = get_cfg80211_regdom()->dfs_region;
@@ -1221,9 +1310,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
reg_process_beacons(wiphy);
reg_process_ht_flags(wiphy);
-
- if (wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, lr);
+ reg_call_notifier(wiphy, lr);
}
static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
@@ -1236,15 +1323,6 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator);
- /*
- * Regulatory updates set by CORE are ignored for custom
- * regulatory cards. Let us notify the changes to the driver,
- * as some drivers used this to restore its orig_* reg domain.
- */
- if (initiator == NL80211_REGDOM_SET_BY_CORE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
- wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, get_last_request());
}
}
@@ -1263,7 +1341,8 @@ static void handle_channel_custom(struct wiphy *wiphy,
if (IS_ERR(reg_rule)) {
REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
chan->center_freq);
- chan->flags = IEEE80211_CHAN_DISABLED;
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
return;
}
@@ -1305,6 +1384,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
enum ieee80211_band band;
unsigned int bands_set = 0;
+ WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG),
+ "wiphy should have REGULATORY_CUSTOM_REG\n");
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!wiphy->bands[band])
continue;
@@ -1320,225 +1403,285 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
}
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
-/* This has the logic which determines when a new request
- * should be ignored. */
-static enum reg_request_treatment
-get_reg_request_treatment(struct wiphy *wiphy,
- struct regulatory_request *pending_request)
+static void reg_set_request_processed(void)
{
- struct wiphy *last_wiphy = NULL;
+ bool need_more_processing = false;
struct regulatory_request *lr = get_last_request();
- /* All initial requests are respected */
- if (!lr)
- return REG_REQ_OK;
+ lr->processed = true;
- switch (pending_request->initiator) {
- case NL80211_REGDOM_SET_BY_CORE:
- return REG_REQ_OK;
- case NL80211_REGDOM_SET_BY_COUNTRY_IE:
- if (reg_request_cell_base(lr)) {
- /* Trust a Cell base station over the AP's country IE */
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_IGNORE;
- return REG_REQ_ALREADY_SET;
- }
+ spin_lock(&reg_requests_lock);
+ if (!list_empty(&reg_requests_list))
+ need_more_processing = true;
+ spin_unlock(&reg_requests_lock);
- last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
+ cancel_delayed_work(&reg_timeout);
- if (unlikely(!is_an_alpha2(pending_request->alpha2)))
- return -EINVAL;
- if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- if (last_wiphy != wiphy) {
- /*
- * Two cards with two APs claiming different
- * Country IE alpha2s. We could
- * intersect them, but that seems unlikely
- * to be correct. Reject second one for now.
- */
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_IGNORE;
- return REG_REQ_ALREADY_SET;
- }
- /*
- * Two consecutive Country IE hints on the same wiphy.
- * This should be picked up early by the driver/stack
- */
- if (WARN_ON(regdom_changes(pending_request->alpha2)))
- return REG_REQ_OK;
- return REG_REQ_ALREADY_SET;
- }
- return REG_REQ_OK;
- case NL80211_REGDOM_SET_BY_DRIVER:
- if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_OK;
- return REG_REQ_ALREADY_SET;
- }
+ if (need_more_processing)
+ schedule_work(&reg_work);
+}
- /*
- * This would happen if you unplug and plug your card
- * back in or if you add a new device for which the previously
- * loaded card also agrees on the regulatory domain.
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
- !regdom_changes(pending_request->alpha2))
- return REG_REQ_ALREADY_SET;
+/**
+ * reg_process_hint_core - process core regulatory requests
+ * @pending_request: a pending core regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by the regulatory core.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_core(struct regulatory_request *core_request)
+{
+
+ core_request->intersect = false;
+ core_request->processed = false;
+
+ reg_update_last_request(core_request);
+
+ return reg_call_crda(core_request);
+}
+static enum reg_request_treatment
+__reg_process_hint_user(struct regulatory_request *user_request)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (reg_request_cell_base(user_request))
+ return reg_ignore_cell_hint(user_request);
+
+ if (reg_request_cell_base(lr))
+ return REG_REQ_IGNORE;
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
return REG_REQ_INTERSECT;
- case NL80211_REGDOM_SET_BY_USER:
- if (reg_request_cell_base(pending_request))
- return reg_ignore_cell_hint(pending_request);
+ /*
+ * If the user knows better the user should set the regdom
+ * to their country before the IE is picked up
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+ lr->intersect)
+ return REG_REQ_IGNORE;
+ /*
+ * Process user requests only after previous user/driver/core
+ * requests have been processed
+ */
+ if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
+ lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+ lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
+ regdom_changes(lr->alpha2))
+ return REG_REQ_IGNORE;
- if (reg_request_cell_base(lr))
- return REG_REQ_IGNORE;
+ if (!regdom_changes(user_request->alpha2))
+ return REG_REQ_ALREADY_SET;
- if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
- return REG_REQ_INTERSECT;
- /*
- * If the user knows better the user should set the regdom
- * to their country before the IE is picked up
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
- lr->intersect)
- return REG_REQ_IGNORE;
- /*
- * Process user requests only after previous user/driver/core
- * requests have been processed
- */
- if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
- lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
- regdom_changes(lr->alpha2))
- return REG_REQ_IGNORE;
+ return REG_REQ_OK;
+}
- if (!regdom_changes(pending_request->alpha2))
- return REG_REQ_ALREADY_SET;
+/**
+ * reg_process_hint_user - process user regulatory requests
+ * @user_request: a pending user regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request initiated by userspace.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_user(struct regulatory_request *user_request)
+{
+ enum reg_request_treatment treatment;
- return REG_REQ_OK;
+ treatment = __reg_process_hint_user(user_request);
+ if (treatment == REG_REQ_IGNORE ||
+ treatment == REG_REQ_ALREADY_SET) {
+ kfree(user_request);
+ return treatment;
}
- return REG_REQ_IGNORE;
+ user_request->intersect = treatment == REG_REQ_INTERSECT;
+ user_request->processed = false;
+
+ reg_update_last_request(user_request);
+
+ user_alpha2[0] = user_request->alpha2[0];
+ user_alpha2[1] = user_request->alpha2[1];
+
+ return reg_call_crda(user_request);
}
-static void reg_set_request_processed(void)
+static enum reg_request_treatment
+__reg_process_hint_driver(struct regulatory_request *driver_request)
{
- bool need_more_processing = false;
struct regulatory_request *lr = get_last_request();
- lr->processed = true;
-
- spin_lock(&reg_requests_lock);
- if (!list_empty(&reg_requests_list))
- need_more_processing = true;
- spin_unlock(&reg_requests_lock);
+ if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
+ if (regdom_changes(driver_request->alpha2))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+ }
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
- cancel_delayed_work(&reg_timeout);
+ /*
+ * This would happen if you unplug and plug your card
+ * back in or if you add a new device for which the previously
+ * loaded card also agrees on the regulatory domain.
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ !regdom_changes(driver_request->alpha2))
+ return REG_REQ_ALREADY_SET;
- if (need_more_processing)
- schedule_work(&reg_work);
+ return REG_REQ_INTERSECT;
}
/**
- * __regulatory_hint - hint to the wireless core a regulatory domain
- * @wiphy: if the hint comes from country information from an AP, this
- * is required to be set to the wiphy that received the information
- * @pending_request: the regulatory request currently being processed
+ * reg_process_hint_driver - process driver regulatory requests
+ * @driver_request: a pending driver regulatory request
*
- * The Wireless subsystem can use this function to hint to the wireless core
- * what it believes should be the current regulatory domain.
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by an 802.11 driver.
*
* Returns one of the different reg request treatment values.
*/
static enum reg_request_treatment
-__regulatory_hint(struct wiphy *wiphy,
- struct regulatory_request *pending_request)
+reg_process_hint_driver(struct wiphy *wiphy,
+ struct regulatory_request *driver_request)
{
const struct ieee80211_regdomain *regd;
- bool intersect = false;
enum reg_request_treatment treatment;
- struct regulatory_request *lr;
- treatment = get_reg_request_treatment(wiphy, pending_request);
+ treatment = __reg_process_hint_driver(driver_request);
switch (treatment) {
- case REG_REQ_INTERSECT:
- if (pending_request->initiator ==
- NL80211_REGDOM_SET_BY_DRIVER) {
- regd = reg_copy_regd(get_cfg80211_regdom());
- if (IS_ERR(regd)) {
- kfree(pending_request);
- return PTR_ERR(regd);
- }
- rcu_assign_pointer(wiphy->regd, regd);
- }
- intersect = true;
- break;
case REG_REQ_OK:
break;
- default:
- /*
- * If the regulatory domain being requested by the
- * driver has already been set just copy it to the
- * wiphy
- */
- if (treatment == REG_REQ_ALREADY_SET &&
- pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
- regd = reg_copy_regd(get_cfg80211_regdom());
- if (IS_ERR(regd)) {
- kfree(pending_request);
- return REG_REQ_IGNORE;
- }
- treatment = REG_REQ_ALREADY_SET;
- rcu_assign_pointer(wiphy->regd, regd);
- goto new_request;
- }
- kfree(pending_request);
+ case REG_REQ_IGNORE:
+ kfree(driver_request);
return treatment;
+ case REG_REQ_INTERSECT:
+ /* fall through */
+ case REG_REQ_ALREADY_SET:
+ regd = reg_copy_regd(get_cfg80211_regdom());
+ if (IS_ERR(regd)) {
+ kfree(driver_request);
+ return REG_REQ_IGNORE;
+ }
+ rcu_assign_pointer(wiphy->regd, regd);
}
-new_request:
- lr = get_last_request();
- if (lr != &core_request_world && lr)
- kfree_rcu(lr, rcu_head);
- pending_request->intersect = intersect;
- pending_request->processed = false;
- rcu_assign_pointer(last_request, pending_request);
- lr = pending_request;
+ driver_request->intersect = treatment == REG_REQ_INTERSECT;
+ driver_request->processed = false;
+
+ reg_update_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;
+ }
+
+ return reg_call_crda(driver_request);
+}
- pending_request = NULL;
+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 (lr->initiator == NL80211_REGDOM_SET_BY_USER) {
- user_alpha2[0] = lr->alpha2[0];
- user_alpha2[1] = lr->alpha2[1];
+ if (reg_request_cell_base(lr)) {
+ /* Trust a Cell base station over the AP's country IE */
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
+ } else {
+ if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE)
+ return REG_REQ_IGNORE;
}
- /* When r == REG_REQ_INTERSECT we do need to call CRDA */
- if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) {
+ if (unlikely(!is_an_alpha2(country_ie_request->alpha2)))
+ return -EINVAL;
+
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)
+ return REG_REQ_OK;
+
+ last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+
+ if (last_wiphy != wiphy) {
/*
- * Since CRDA will not be called in this case as we already
- * have applied the requested regulatory domain before we just
- * inform userspace we have processed the request
+ * Two cards with two APs claiming different
+ * Country IE alpha2s. We could
+ * intersect them, but that seems unlikely
+ * to be correct. Reject second one for now.
*/
- if (treatment == REG_REQ_ALREADY_SET) {
- nl80211_send_reg_change_event(lr);
- reg_set_request_processed();
- }
- return treatment;
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
}
+ /*
+ * Two consecutive Country IE hints on the same wiphy.
+ * This should be picked up early by the driver/stack
+ */
+ if (WARN_ON(regdom_changes(country_ie_request->alpha2)))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+}
- if (call_crda(lr->alpha2))
+/**
+ * reg_process_hint_country_ie - process regulatory requests from country IEs
+ * @country_ie_request: a regulatory request from a country IE
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by a country Information Element.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_country_ie(struct wiphy *wiphy,
+ struct regulatory_request *country_ie_request)
+{
+ enum reg_request_treatment treatment;
+
+ treatment = __reg_process_hint_country_ie(wiphy, country_ie_request);
+
+ switch (treatment) {
+ case REG_REQ_OK:
+ break;
+ case REG_REQ_IGNORE:
+ /* fall through */
+ case REG_REQ_ALREADY_SET:
+ kfree(country_ie_request);
+ return treatment;
+ case REG_REQ_INTERSECT:
+ kfree(country_ie_request);
+ /*
+ * This doesn't happen yet, not sure we
+ * ever want to support it for this case.
+ */
+ WARN_ONCE(1, "Unexpected intersection for country IEs");
return REG_REQ_IGNORE;
- return REG_REQ_OK;
+ }
+
+ country_ie_request->intersect = false;
+ country_ie_request->processed = false;
+
+ reg_update_last_request(country_ie_request);
+
+ return reg_call_crda(country_ie_request);
}
/* This processes *all* regulatory hints */
-static void reg_process_hint(struct regulatory_request *reg_request,
- enum nl80211_reg_initiator reg_initiator)
+static void reg_process_hint(struct regulatory_request *reg_request)
{
struct wiphy *wiphy = NULL;
+ enum reg_request_treatment treatment;
if (WARN_ON(!reg_request->alpha2))
return;
@@ -1546,23 +1689,37 @@ static void reg_process_hint(struct regulatory_request *reg_request,
if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
- if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
+ if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
kfree(reg_request);
return;
}
- switch (__regulatory_hint(wiphy, reg_request)) {
- case REG_REQ_ALREADY_SET:
- /* This is required so that the orig_* parameters are saved */
- if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
- wiphy_update_regulatory(wiphy, reg_initiator);
+ switch (reg_request->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ reg_process_hint_core(reg_request);
+ return;
+ case NL80211_REGDOM_SET_BY_USER:
+ treatment = reg_process_hint_user(reg_request);
+ if (treatment == REG_REQ_OK ||
+ treatment == REG_REQ_ALREADY_SET)
+ return;
+ schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
+ return;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ treatment = reg_process_hint_driver(wiphy, reg_request);
break;
- default:
- if (reg_initiator == NL80211_REGDOM_SET_BY_USER)
- schedule_delayed_work(&reg_timeout,
- msecs_to_jiffies(3142));
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ treatment = reg_process_hint_country_ie(wiphy, reg_request);
break;
+ default:
+ WARN(1, "invalid initiator %d\n", reg_request->initiator);
+ return;
}
+
+ /* This is required so that the orig_* parameters are saved */
+ if (treatment == REG_REQ_ALREADY_SET && wiphy &&
+ wiphy->regulatory_flags & REGULATORY_STRICT_REG)
+ wiphy_update_regulatory(wiphy, reg_request->initiator);
}
/*
@@ -1596,7 +1753,7 @@ static void reg_process_pending_hints(void)
spin_unlock(&reg_requests_lock);
- reg_process_hint(reg_request, reg_request->initiator);
+ reg_process_hint(reg_request);
}
/* Processes beacon hints -- this has nothing to do with country IEs */
@@ -1696,6 +1853,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
if (WARN_ON(!alpha2 || !wiphy))
return -EINVAL;
+ wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG;
+
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
if (!request)
return -ENOMEM;
@@ -1888,7 +2047,7 @@ static void restore_regulatory_settings(bool reset_user)
world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
- if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+ if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
restore_custom_reg_settings(&rdev->wiphy);
}
@@ -2016,7 +2175,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
}
}
-bool reg_supported_dfs_region(u8 dfs_region)
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
{
switch (dfs_region) {
case NL80211_DFS_UNSET:
@@ -2031,27 +2190,6 @@ bool reg_supported_dfs_region(u8 dfs_region)
}
}
-static void print_dfs_region(u8 dfs_region)
-{
- if (!dfs_region)
- return;
-
- switch (dfs_region) {
- case NL80211_DFS_FCC:
- pr_info(" DFS Master region FCC");
- break;
- case NL80211_DFS_ETSI:
- pr_info(" DFS Master region ETSI");
- break;
- case NL80211_DFS_JP:
- pr_info(" DFS Master region JP");
- break;
- default:
- pr_info(" DFS Master region Unknown");
- break;
- }
-}
-
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
struct regulatory_request *lr = get_last_request();
@@ -2083,7 +2221,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
}
}
- print_dfs_region(rd->dfs_region);
+ pr_info(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));
print_rd_rules(rd);
}
@@ -2093,48 +2231,60 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
print_rd_rules(rd);
}
-/* Takes ownership of rd only if it doesn't fail */
-static int __set_regdom(const struct ieee80211_regdomain *rd)
+static int reg_set_rd_core(const struct ieee80211_regdomain *rd)
+{
+ if (!is_world_regdom(rd->alpha2))
+ return -EINVAL;
+ update_world_regdomain(rd);
+ return 0;
+}
+
+static int reg_set_rd_user(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *user_request)
{
- const struct ieee80211_regdomain *regd;
const struct ieee80211_regdomain *intersected_rd = NULL;
- struct wiphy *request_wiphy;
- struct regulatory_request *lr = get_last_request();
- /* Some basic sanity checks first */
+ if (is_world_regdom(rd->alpha2))
+ return -EINVAL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
- if (!reg_is_valid_request(rd->alpha2))
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected:\n");
+ print_regdomain_info(rd);
return -EINVAL;
+ }
- if (is_world_regdom(rd->alpha2)) {
- update_world_regdomain(rd);
+ if (!user_request->intersect) {
+ reset_regdomains(false, rd);
return 0;
}
- if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
- !is_unknown_alpha2(rd->alpha2))
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
return -EINVAL;
- /*
- * Lets only bother proceeding on the same alpha2 if the current
- * rd is non static (it means CRDA was present and was used last)
- * and the pending request came in from a country IE
- */
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- /*
- * If someone else asked us to change the rd lets only bother
- * checking if the alpha2 changes if CRDA was already called
- */
- if (!regdom_changes(rd->alpha2))
- return -EALREADY;
- }
+ kfree(rd);
+ rd = NULL;
+ reset_regdomains(false, intersected_rd);
- /*
- * Now lets set the regulatory domain, update all driver channels
- * and finally inform them of what we have done, in case they want
- * to review or adjust their own settings based on their own
- * internal EEPROM data
- */
+ return 0;
+}
+
+static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *driver_request)
+{
+ const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+ const struct ieee80211_regdomain *tmp;
+ struct wiphy *request_wiphy;
+
+ if (is_world_regdom(rd->alpha2))
+ return -EINVAL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
if (!is_valid_rd(rd)) {
pr_err("Invalid regulatory domain detected:\n");
@@ -2142,29 +2292,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return -EINVAL;
}
- request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
- if (!request_wiphy &&
- (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
+ request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
+ if (!request_wiphy) {
schedule_delayed_work(&reg_timeout, 0);
return -ENODEV;
}
- if (!lr->intersect) {
- if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
- reset_regdomains(false, rd);
- return 0;
- }
-
- /*
- * For a driver hint, lets copy the regulatory domain the
- * driver wanted to the wiphy to deal with conflicts
- */
-
- /*
- * Userspace could have sent two replies with only
- * one kernel request.
- */
+ if (!driver_request->intersect) {
if (request_wiphy->regd)
return -EALREADY;
@@ -2177,38 +2311,59 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return 0;
}
- /* Intersection requires a bit more work */
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
+ return -EINVAL;
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
- if (!intersected_rd)
- return -EINVAL;
+ /*
+ * We can trash what CRDA provided now.
+ * However if a driver requested this specific regulatory
+ * domain we keep it for its private use
+ */
+ tmp = get_wiphy_regdom(request_wiphy);
+ rcu_assign_pointer(request_wiphy->regd, rd);
+ rcu_free_regdom(tmp);
- /*
- * We can trash what CRDA provided now.
- * However if a driver requested this specific regulatory
- * domain we keep it for its private use
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
- const struct ieee80211_regdomain *tmp;
+ rd = NULL;
- tmp = get_wiphy_regdom(request_wiphy);
- rcu_assign_pointer(request_wiphy->regd, rd);
- rcu_free_regdom(tmp);
- } else {
- kfree(rd);
- }
+ reset_regdomains(false, intersected_rd);
+
+ return 0;
+}
- rd = NULL;
+static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *country_ie_request)
+{
+ 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(country_ie_request->wiphy_idx);
+ if (!request_wiphy) {
+ schedule_delayed_work(&reg_timeout, 0);
+ return -ENODEV;
+ }
+
+ if (country_ie_request->intersect)
+ return -EINVAL;
+ reset_regdomains(false, rd);
+ return 0;
+}
/*
* Use this call to set the current regulatory domain. Conflicts with
@@ -2220,10 +2375,32 @@ int set_regdom(const struct ieee80211_regdomain *rd)
struct regulatory_request *lr;
int r;
+ if (!reg_is_valid_request(rd->alpha2)) {
+ kfree(rd);
+ return -EINVAL;
+ }
+
lr = get_last_request();
/* Note that this doesn't update the wiphys, this is done below */
- r = __set_regdom(rd);
+ switch (lr->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ r = reg_set_rd_core(rd);
+ break;
+ case NL80211_REGDOM_SET_BY_USER:
+ r = reg_set_rd_user(rd, lr);
+ break;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ r = reg_set_rd_driver(rd, lr);
+ break;
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ r = reg_set_rd_country_ie(rd, lr);
+ break;
+ default:
+ WARN(1, "invalid initiator %d\n", lr->initiator);
+ return -EINVAL;
+ }
+
if (r) {
if (r == -EALREADY)
reg_set_request_processed();
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 9677e3c13da9..02bd8f4b0921 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -18,8 +18,10 @@
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);
+enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index d4397eba5408..b528e31da2cf 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -161,7 +161,7 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
dev->bss_generation++;
}
-void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
+void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
{
struct cfg80211_scan_request *request;
struct wireless_dev *wdev;
@@ -210,17 +210,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
dev_put(wdev->netdev);
rdev->scan_req = NULL;
-
- /*
- * OK. If this is invoked with "leak" then we can't
- * free this ... but we've cleaned it up anyway. The
- * driver failed to call the scan_done callback, so
- * all bets are off, it might still be trying to use
- * the scan request or not ... if it accesses the dev
- * in there (it shouldn't anyway) then it may crash.
- */
- if (!leak)
- kfree(request);
+ kfree(request);
}
void __cfg80211_scan_done(struct work_struct *wk)
@@ -231,7 +221,7 @@ void __cfg80211_scan_done(struct work_struct *wk)
scan_done_wk);
rtnl_lock();
- ___cfg80211_scan_done(rdev, false);
+ ___cfg80211_scan_done(rdev);
rtnl_unlock();
}
@@ -1099,11 +1089,8 @@ int cfg80211_wext_siwscan(struct net_device *dev,
/* Determine number of channels, needed to allocate creq */
if (wreq && wreq->num_channels)
n_channels = wreq->num_channels;
- else {
- for (band = 0; band < IEEE80211_NUM_BANDS; band++)
- if (wiphy->bands[band])
- n_channels += wiphy->bands[band]->n_channels;
- }
+ else
+ n_channels = ieee80211_get_num_supported_channels(wiphy);
creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
n_channels * sizeof(void *),
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index d3c5bd7c6b51..a63509118508 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -70,18 +70,11 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
if (rdev->scan_req)
return -EBUSY;
- if (wdev->conn->params.channel) {
+ if (wdev->conn->params.channel)
n_channels = 1;
- } else {
- enum ieee80211_band band;
- n_channels = 0;
+ else
+ n_channels = ieee80211_get_num_supported_channels(wdev->wiphy);
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- if (!wdev->wiphy->bands[band])
- continue;
- n_channels += wdev->wiphy->bands[band]->n_channels;
- }
- }
request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
sizeof(request->channels[0]) * n_channels,
GFP_KERNEL);
@@ -872,6 +865,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
for (i = 0; i < 6; i++)
rdev_del_key(rdev, dev, i, false, NULL);
+ rdev_set_qos_map(rdev, dev, NULL);
+
#ifdef CONFIG_CFG80211_WEXT
memset(&wrqu, 0, sizeof(wrqu));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index ba5f0d6614d5..fbcc23edee54 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -186,6 +186,28 @@
#define BOOL_TO_STR(bo) (bo) ? "true" : "false"
+#define QOS_MAP_ENTRY __field(u8, num_des) \
+ __array(u8, dscp_exception, \
+ 2 * IEEE80211_QOS_MAP_MAX_EX) \
+ __array(u8, up, IEEE80211_QOS_MAP_LEN_MIN)
+#define QOS_MAP_ASSIGN(qos_map) \
+ do { \
+ if ((qos_map)) { \
+ __entry->num_des = (qos_map)->num_des; \
+ memcpy(__entry->dscp_exception, \
+ &(qos_map)->dscp_exception, \
+ 2 * IEEE80211_QOS_MAP_MAX_EX); \
+ memcpy(__entry->up, &(qos_map)->up, \
+ IEEE80211_QOS_MAP_LEN_MIN); \
+ } else { \
+ __entry->num_des = 0; \
+ memset(__entry->dscp_exception, 0, \
+ 2 * IEEE80211_QOS_MAP_MAX_EX); \
+ memset(__entry->up, 0, \
+ IEEE80211_QOS_MAP_LEN_MIN); \
+ } \
+ } while (0)
+
/*************************************************************
* rdev->ops traces *
*************************************************************/
@@ -1653,9 +1675,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 +1689,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",
@@ -1876,6 +1897,24 @@ TRACE_EVENT(rdev_channel_switch,
__entry->counter_offset_presp)
);
+TRACE_EVENT(rdev_set_qos_map,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_qos_map *qos_map),
+ TP_ARGS(wiphy, netdev, qos_map),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ QOS_MAP_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ QOS_MAP_ASSIGN(qos_map);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", num_des: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des)
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 935dea9485da..d39c37104ae2 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -689,7 +689,8 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
/* Given a data frame determine the 802.1p/1d tag to use. */
-unsigned int cfg80211_classify8021d(struct sk_buff *skb)
+unsigned int cfg80211_classify8021d(struct sk_buff *skb,
+ struct cfg80211_qos_map *qos_map)
{
unsigned int dscp;
unsigned char vlan_priority;
@@ -720,6 +721,21 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb)
return 0;
}
+ if (qos_map) {
+ unsigned int i, tmp_dscp = dscp >> 2;
+
+ for (i = 0; i < qos_map->num_des; i++) {
+ if (tmp_dscp == qos_map->dscp_exception[i].dscp)
+ return qos_map->dscp_exception[i].up;
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (tmp_dscp >= qos_map->up[i].low &&
+ tmp_dscp <= qos_map->up[i].high)
+ return i;
+ }
+ }
+
return dscp >> 5;
}
EXPORT_SYMBOL(cfg80211_classify8021d);
@@ -863,6 +879,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
dev->ieee80211_ptr->use_4addr = false;
dev->ieee80211_ptr->mesh_id_up_len = 0;
+ wdev_lock(dev->ieee80211_ptr);
+ rdev_set_qos_map(rdev, dev, NULL);
+ wdev_unlock(dev->ieee80211_ptr);
switch (otype) {
case NL80211_IFTYPE_AP:
@@ -1462,6 +1481,19 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
return 0;
}
+unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy)
+{
+ enum ieee80211_band band;
+ unsigned int n_channels = 0;
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+ if (wiphy->bands[band])
+ n_channels += wiphy->bands[band]->n_channels;
+
+ return n_channels;
+}
+EXPORT_SYMBOL(ieee80211_get_num_supported_channels);
+
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
const unsigned char rfc1042_header[] __aligned(2) =
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index e7c6e862580d..5661a54ac7ee 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -370,7 +370,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,
u8 oshort = wdev->wiphy->retry_short;
int err;
- if (retry->disabled ||
+ if (retry->disabled || retry->value < 1 || retry->value > 255 ||
(retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
return -EINVAL;
@@ -412,9 +412,9 @@ int cfg80211_wext_giwretry(struct net_device *dev,
* First return short value, iwconfig will ask long value
* later if needed
*/
- retry->flags |= IW_RETRY_LIMIT;
+ retry->flags |= IW_RETRY_LIMIT | IW_RETRY_SHORT;
retry->value = wdev->wiphy->retry_short;
- if (wdev->wiphy->retry_long != wdev->wiphy->retry_short)
+ if (wdev->wiphy->retry_long == wdev->wiphy->retry_short)
retry->flags |= IW_RETRY_LONG;
return 0;