diff options
Diffstat (limited to 'net/wireless')
| -rw-r--r-- | net/wireless/Kconfig | 5 | ||||
| -rw-r--r-- | net/wireless/Makefile | 4 | ||||
| -rw-r--r-- | net/wireless/ap.c | 66 | ||||
| -rw-r--r-- | net/wireless/chan.c | 455 | ||||
| -rw-r--r-- | net/wireless/core.c | 62 | ||||
| -rw-r--r-- | net/wireless/core.h | 92 | ||||
| -rw-r--r-- | net/wireless/ethtool.c | 19 | ||||
| -rw-r--r-- | net/wireless/ibss.c | 40 | ||||
| -rw-r--r-- | net/wireless/mesh.c | 67 | ||||
| -rw-r--r-- | net/wireless/mlme.c | 232 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 1944 | ||||
| -rw-r--r-- | net/wireless/nl80211.h | 15 | ||||
| -rw-r--r-- | net/wireless/rdev-ops.h | 890 | ||||
| -rw-r--r-- | net/wireless/reg.c | 1154 | ||||
| -rw-r--r-- | net/wireless/reg.h | 9 | ||||
| -rw-r--r-- | net/wireless/scan.c | 1017 | ||||
| -rw-r--r-- | net/wireless/sme.c | 50 | ||||
| -rw-r--r-- | net/wireless/sysfs.c | 11 | ||||
| -rw-r--r-- | net/wireless/trace.c | 7 | ||||
| -rw-r--r-- | net/wireless/trace.h | 2422 | ||||
| -rw-r--r-- | net/wireless/util.c | 239 | ||||
| -rw-r--r-- | net/wireless/wext-compat.c | 76 | ||||
| -rw-r--r-- | net/wireless/wext-proc.c | 5 | ||||
| -rw-r--r-- | net/wireless/wext-sme.c | 19 | 
24 files changed, 7063 insertions, 1837 deletions
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index fe4adb12b3e..16d08b39921 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -140,14 +140,13 @@ config CFG80211_WEXT  	  extensions with cfg80211-based drivers.  config LIB80211 -	tristate "Common routines for IEEE802.11 drivers" +	tristate  	default n  	help  	  This options enables a library of common routines used  	  by IEEE802.11 wireless LAN drivers. -	  Drivers should select this themselves if needed.  Say Y if -	  you want this built into your kernel. +	  Drivers should select this themselves if needed.  config LIB80211_CRYPT_WEP  	tristate diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 0f7e0d621ab..a761670af31 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -10,11 +10,13 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o  obj-$(CONFIG_WEXT_PRIV) += wext-priv.o  cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o -cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o  cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o  cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o  cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o +CFLAGS_trace.o := -I$(src) +  ccflags-y += -D__CHECK_ENDIAN__  $(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk diff --git a/net/wireless/ap.c b/net/wireless/ap.c index fcc60d8dbef..a4a14e8f55c 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -3,6 +3,7 @@  #include <net/cfg80211.h>  #include "nl80211.h"  #include "core.h" +#include "rdev-ops.h"  static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, @@ -23,10 +24,11 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,  	if (!wdev->beacon_interval)  		return -ENOENT; -	err = rdev->ops->stop_ap(&rdev->wiphy, dev); +	err = rdev_stop_ap(rdev, dev);  	if (!err) {  		wdev->beacon_interval = 0;  		wdev->channel = NULL; +		wdev->ssid_len = 0;  	}  	return err; @@ -44,3 +46,65 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,  	return err;  } + +void cfg80211_ch_switch_notify(struct net_device *dev, +			       struct cfg80211_chan_def *chandef) +{ +	struct wireless_dev *wdev = dev->ieee80211_ptr; +	struct wiphy *wiphy = wdev->wiphy; +	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + +	trace_cfg80211_ch_switch_notify(dev, chandef); + +	wdev_lock(wdev); + +	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && +		    wdev->iftype != NL80211_IFTYPE_P2P_GO)) +		goto out; + +	wdev->channel = chandef->chan; +	nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); +out: +	wdev_unlock(wdev); +	return; +} +EXPORT_SYMBOL(cfg80211_ch_switch_notify); + +bool cfg80211_rx_spurious_frame(struct net_device *dev, +				const u8 *addr, gfp_t gfp) +{ +	struct wireless_dev *wdev = dev->ieee80211_ptr; +	bool ret; + +	trace_cfg80211_rx_spurious_frame(dev, addr); + +	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && +		    wdev->iftype != NL80211_IFTYPE_P2P_GO)) { +		trace_cfg80211_return_bool(false); +		return false; +	} +	ret = nl80211_unexpected_frame(dev, addr, gfp); +	trace_cfg80211_return_bool(ret); +	return ret; +} +EXPORT_SYMBOL(cfg80211_rx_spurious_frame); + +bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, +					const u8 *addr, gfp_t gfp) +{ +	struct wireless_dev *wdev = dev->ieee80211_ptr; +	bool ret; + +	trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); + +	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && +		    wdev->iftype != NL80211_IFTYPE_P2P_GO && +		    wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { +		trace_cfg80211_return_bool(false); +		return false; +	} +	ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); +	trace_cfg80211_return_bool(ret); +	return ret; +} +EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 2f876b9ee34..fd556ac05fd 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -9,90 +9,440 @@  #include <linux/export.h>  #include <net/cfg80211.h>  #include "core.h" +#include "rdev-ops.h" -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, -		  int freq, enum nl80211_channel_type channel_type) +void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, +			     struct ieee80211_channel *chan, +			     enum nl80211_channel_type chan_type)  { -	struct ieee80211_channel *chan; -	struct ieee80211_sta_ht_cap *ht_cap; +	if (WARN_ON(!chan)) +		return; + +	chandef->chan = chan; +	chandef->center_freq2 = 0; + +	switch (chan_type) { +	case NL80211_CHAN_NO_HT: +		chandef->width = NL80211_CHAN_WIDTH_20_NOHT; +		chandef->center_freq1 = chan->center_freq; +		break; +	case NL80211_CHAN_HT20: +		chandef->width = NL80211_CHAN_WIDTH_20; +		chandef->center_freq1 = chan->center_freq; +		break; +	case NL80211_CHAN_HT40PLUS: +		chandef->width = NL80211_CHAN_WIDTH_40; +		chandef->center_freq1 = chan->center_freq + 10; +		break; +	case NL80211_CHAN_HT40MINUS: +		chandef->width = NL80211_CHAN_WIDTH_40; +		chandef->center_freq1 = chan->center_freq - 10; +		break; +	default: +		WARN_ON(1); +	} +} +EXPORT_SYMBOL(cfg80211_chandef_create); + +bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) +{ +	u32 control_freq; -	chan = ieee80211_get_channel(&rdev->wiphy, freq); +	if (!chandef->chan) +		return false; + +	control_freq = chandef->chan->center_freq; + +	switch (chandef->width) { +	case NL80211_CHAN_WIDTH_20: +	case NL80211_CHAN_WIDTH_20_NOHT: +		if (chandef->center_freq1 != control_freq) +			return false; +		if (chandef->center_freq2) +			return false; +		break; +	case NL80211_CHAN_WIDTH_40: +		if (chandef->center_freq1 != control_freq + 10 && +		    chandef->center_freq1 != control_freq - 10) +			return false; +		if (chandef->center_freq2) +			return false; +		break; +	case NL80211_CHAN_WIDTH_80P80: +		if (chandef->center_freq1 != control_freq + 30 && +		    chandef->center_freq1 != control_freq + 10 && +		    chandef->center_freq1 != control_freq - 10 && +		    chandef->center_freq1 != control_freq - 30) +			return false; +		if (!chandef->center_freq2) +			return false; +		/* adjacent is not allowed -- that's a 160 MHz channel */ +		if (chandef->center_freq1 - chandef->center_freq2 == 80 || +		    chandef->center_freq2 - chandef->center_freq1 == 80) +			return false; +		break; +	case NL80211_CHAN_WIDTH_80: +		if (chandef->center_freq1 != control_freq + 30 && +		    chandef->center_freq1 != control_freq + 10 && +		    chandef->center_freq1 != control_freq - 10 && +		    chandef->center_freq1 != control_freq - 30) +			return false; +		if (chandef->center_freq2) +			return false; +		break; +	case NL80211_CHAN_WIDTH_160: +		if (chandef->center_freq1 != control_freq + 70 && +		    chandef->center_freq1 != control_freq + 50 && +		    chandef->center_freq1 != control_freq + 30 && +		    chandef->center_freq1 != control_freq + 10 && +		    chandef->center_freq1 != control_freq - 10 && +		    chandef->center_freq1 != control_freq - 30 && +		    chandef->center_freq1 != control_freq - 50 && +		    chandef->center_freq1 != control_freq - 70) +			return false; +		if (chandef->center_freq2) +			return false; +		break; +	default: +		return false; +	} + +	return true; +} +EXPORT_SYMBOL(cfg80211_chandef_valid); + +static void chandef_primary_freqs(const struct cfg80211_chan_def *c, +				  int *pri40, int *pri80) +{ +	int tmp; -	/* Primary channel not allowed */ -	if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) +	switch (c->width) { +	case NL80211_CHAN_WIDTH_40: +		*pri40 = c->center_freq1; +		*pri80 = 0; +		break; +	case NL80211_CHAN_WIDTH_80: +	case NL80211_CHAN_WIDTH_80P80: +		*pri80 = c->center_freq1; +		/* n_P20 */ +		tmp = (30 + c->chan->center_freq - c->center_freq1)/20; +		/* n_P40 */ +		tmp /= 2; +		/* freq_P40 */ +		*pri40 = c->center_freq1 - 20 + 40 * tmp; +		break; +	case NL80211_CHAN_WIDTH_160: +		/* n_P20 */ +		tmp = (70 + c->chan->center_freq - c->center_freq1)/20; +		/* n_P40 */ +		tmp /= 2; +		/* freq_P40 */ +		*pri40 = c->center_freq1 - 60 + 40 * tmp; +		/* n_P80 */ +		tmp /= 2; +		*pri80 = c->center_freq1 - 40 + 80 * tmp; +		break; +	default: +		WARN_ON_ONCE(1); +	} +} + +static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c) +{ +	int width; + +	switch (c->width) { +	case NL80211_CHAN_WIDTH_20: +	case NL80211_CHAN_WIDTH_20_NOHT: +		width = 20; +		break; +	case NL80211_CHAN_WIDTH_40: +		width = 40; +		break; +	case NL80211_CHAN_WIDTH_80P80: +	case NL80211_CHAN_WIDTH_80: +		width = 80; +		break; +	case NL80211_CHAN_WIDTH_160: +		width = 160; +		break; +	default: +		WARN_ON_ONCE(1); +		return -1; +	} +	return width; +} + +const struct cfg80211_chan_def * +cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, +			    const struct cfg80211_chan_def *c2) +{ +	u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80; + +	/* If they are identical, return */ +	if (cfg80211_chandef_identical(c1, c2)) +		return c1; + +	/* otherwise, must have same control channel */ +	if (c1->chan != c2->chan)  		return NULL; -	if (channel_type == NL80211_CHAN_HT40MINUS && -	    chan->flags & IEEE80211_CHAN_NO_HT40MINUS) +	/* +	 * If they have the same width, but aren't identical, +	 * then they can't be compatible. +	 */ +	if (c1->width == c2->width)  		return NULL; -	else if (channel_type == NL80211_CHAN_HT40PLUS && -		 chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + +	if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || +	    c1->width == NL80211_CHAN_WIDTH_20) +		return c2; + +	if (c2->width == NL80211_CHAN_WIDTH_20_NOHT || +	    c2->width == NL80211_CHAN_WIDTH_20) +		return c1; + +	chandef_primary_freqs(c1, &c1_pri40, &c1_pri80); +	chandef_primary_freqs(c2, &c2_pri40, &c2_pri80); + +	if (c1_pri40 != c2_pri40)  		return NULL; -	ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; +	WARN_ON(!c1_pri80 && !c2_pri80); +	if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80) +		return NULL; -	if (channel_type != NL80211_CHAN_NO_HT) { -		if (!ht_cap->ht_supported) -			return NULL; +	if (c1->width > c2->width) +		return c1; +	return c2; +} +EXPORT_SYMBOL(cfg80211_chandef_compatible); -		if (channel_type != NL80211_CHAN_HT20 && -		    (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || -		    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) -			return NULL; +static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq, +					 u32 bandwidth, +					 enum nl80211_dfs_state dfs_state) +{ +	struct ieee80211_channel *c; +	u32 freq; + +	for (freq = center_freq - bandwidth/2 + 10; +	     freq <= center_freq + bandwidth/2 - 10; +	     freq += 20) { +		c = ieee80211_get_channel(wiphy, freq); +		if (!c || !(c->flags & IEEE80211_CHAN_RADAR)) +			continue; + +		c->dfs_state = dfs_state; +		c->dfs_state_entered = jiffies;  	} +} -	return chan; +void cfg80211_set_dfs_state(struct wiphy *wiphy, +			    const struct cfg80211_chan_def *chandef, +			    enum nl80211_dfs_state dfs_state) +{ +	int width; + +	if (WARN_ON(!cfg80211_chandef_valid(chandef))) +		return; + +	width = cfg80211_chandef_get_width(chandef); +	if (width < 0) +		return; + +	cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1, +				     width, dfs_state); + +	if (!chandef->center_freq2) +		return; +	cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2, +				     width, dfs_state);  } -bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, -				  struct ieee80211_channel *chan, -				  enum nl80211_channel_type channel_type) +static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, +					    u32 center_freq, +					    u32 bandwidth)  { -	struct ieee80211_channel *sec_chan; -	int diff; +	struct ieee80211_channel *c; +	u32 freq; -	switch (channel_type) { -	case NL80211_CHAN_HT40PLUS: -		diff = 20; +	for (freq = center_freq - bandwidth/2 + 10; +	     freq <= center_freq + bandwidth/2 - 10; +	     freq += 20) { +		c = ieee80211_get_channel(wiphy, freq); +		if (!c) +			return -EINVAL; + +		if (c->flags & IEEE80211_CHAN_RADAR) +			return 1; +	} +	return 0; +} + + +int cfg80211_chandef_dfs_required(struct wiphy *wiphy, +				  const struct cfg80211_chan_def *chandef) +{ +	int width; +	int r; + +	if (WARN_ON(!cfg80211_chandef_valid(chandef))) +		return -EINVAL; + +	width = cfg80211_chandef_get_width(chandef); +	if (width < 0) +		return -EINVAL; + +	r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1, +					    width); +	if (r) +		return r; + +	if (!chandef->center_freq2) +		return 0; + +	return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, +					       width); +} + +static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, +					u32 center_freq, u32 bandwidth, +					u32 prohibited_flags) +{ +	struct ieee80211_channel *c; +	u32 freq; + +	for (freq = center_freq - bandwidth/2 + 10; +	     freq <= center_freq + bandwidth/2 - 10; +	     freq += 20) { +		c = ieee80211_get_channel(wiphy, freq); +		if (!c) +			return false; + +		/* check for radar flags */ +		if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) && +		    (c->dfs_state != NL80211_DFS_AVAILABLE)) +			return false; + +		/* check for the other flags */ +		if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR) +			return false; +	} + +	return true; +} + +bool cfg80211_chandef_usable(struct wiphy *wiphy, +			     const struct cfg80211_chan_def *chandef, +			     u32 prohibited_flags) +{ +	struct ieee80211_sta_ht_cap *ht_cap; +	struct ieee80211_sta_vht_cap *vht_cap; +	u32 width, control_freq; + +	if (WARN_ON(!cfg80211_chandef_valid(chandef))) +		return false; + +	ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; +	vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; + +	control_freq = chandef->chan->center_freq; + +	switch (chandef->width) { +	case NL80211_CHAN_WIDTH_20: +		if (!ht_cap->ht_supported) +			return false; +	case NL80211_CHAN_WIDTH_20_NOHT: +		width = 20;  		break; -	case NL80211_CHAN_HT40MINUS: -		diff = -20; +	case NL80211_CHAN_WIDTH_40: +		width = 40; +		if (!ht_cap->ht_supported) +			return false; +		if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || +		    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) +			return false; +		if (chandef->center_freq1 < control_freq && +		    chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) +			return false; +		if (chandef->center_freq1 > control_freq && +		    chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) +			return false; +		break; +	case NL80211_CHAN_WIDTH_80P80: +		if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) +			return false; +	case NL80211_CHAN_WIDTH_80: +		if (!vht_cap->vht_supported) +			return false; +		prohibited_flags |= IEEE80211_CHAN_NO_80MHZ; +		width = 80; +		break; +	case NL80211_CHAN_WIDTH_160: +		if (!vht_cap->vht_supported) +			return false; +		if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) +			return false; +		prohibited_flags |= IEEE80211_CHAN_NO_160MHZ; +		width = 160;  		break;  	default: -		return true; +		WARN_ON_ONCE(1); +		return false;  	} -	sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); -	if (!sec_chan) -		return false; +	/* +	 * TODO: What if there are only certain 80/160/80+80 MHz channels +	 *	 allowed by the driver, or only certain combinations? +	 *	 For 40 MHz the driver can set the NO_HT40 flags, but for +	 *	 80/160 MHz and in particular 80+80 MHz this isn't really +	 *	 feasible and we only have NO_80MHZ/NO_160MHZ so far but +	 *	 no way to cover 80+80 MHz or more complex restrictions. +	 *	 Note that such restrictions also need to be advertised to +	 *	 userspace, for example for P2P channel selection. +	 */ + +	if (width > 20) +		prohibited_flags |= IEEE80211_CHAN_NO_OFDM; -	/* we'll need a DFS capability later */ -	if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | -			       IEEE80211_CHAN_PASSIVE_SCAN | -			       IEEE80211_CHAN_NO_IBSS | -			       IEEE80211_CHAN_RADAR)) +	if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1, +					 width, prohibited_flags))  		return false; -	return true; +	if (!chandef->center_freq2) +		return true; +	return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2, +					   width, prohibited_flags);  } -EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan); +EXPORT_SYMBOL(cfg80211_chandef_usable); -int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, -				 int freq, enum nl80211_channel_type chantype) +bool cfg80211_reg_can_beacon(struct wiphy *wiphy, +			     struct cfg80211_chan_def *chandef)  { -	struct ieee80211_channel *chan; +	bool res; + +	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); + +	trace_cfg80211_return_bool(res); +	return res; +} +EXPORT_SYMBOL(cfg80211_reg_can_beacon); + +int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, +				 struct cfg80211_chan_def *chandef) +{  	if (!rdev->ops->set_monitor_channel)  		return -EOPNOTSUPP;  	if (!cfg80211_has_monitors_only(rdev))  		return -EBUSY; -	chan = rdev_freq_to_chan(rdev, freq, chantype); -	if (!chan) -		return -EINVAL; - -	return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); +	return rdev_set_monitor_channel(rdev, chandef);  }  void @@ -127,7 +477,10 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,  		break;  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_P2P_GO: -		if (wdev->beacon_interval) { +		if (wdev->cac_started) { +			*chan = wdev->channel; +			*chanmode = CHAN_MODE_SHARED; +		} else if (wdev->beacon_interval) {  			*chan = wdev->channel;  			*chanmode = CHAN_MODE_SHARED;  		} diff --git a/net/wireless/core.c b/net/wireless/core.c index 3f725305208..ea4155fe973 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -26,6 +26,7 @@  #include "debugfs.h"  #include "wext-compat.h"  #include "ethtool.h" +#include "rdev-ops.h"  /* name for sysfs, %d is appended */  #define PHY_NAME "phy" @@ -56,9 +57,6 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)  {  	struct cfg80211_registered_device *result = NULL, *rdev; -	if (!wiphy_idx_valid(wiphy_idx)) -		return NULL; -  	assert_cfg80211_lock();  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) { @@ -73,10 +71,8 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)  int get_wiphy_idx(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev; -	if (!wiphy) -		return WIPHY_IDX_STALE; -	rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +  	return rdev->wiphy_idx;  } @@ -85,9 +81,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)  {  	struct cfg80211_registered_device *rdev; -	if (!wiphy_idx_valid(wiphy_idx)) -		return NULL; -  	assert_cfg80211_lock();  	rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); @@ -216,7 +209,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)  {  	struct cfg80211_registered_device *rdev = data; -	rdev->ops->rfkill_poll(&rdev->wiphy); +	rdev_rfkill_poll(rdev);  }  static int cfg80211_rfkill_set_block(void *data, bool blocked) @@ -240,7 +233,7 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)  		case NL80211_IFTYPE_P2P_DEVICE:  			if (!wdev->p2p_started)  				break; -			rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); +			rdev_stop_p2p_device(rdev, wdev);  			wdev->p2p_started = false;  			rdev->opencount--;  			break; @@ -308,7 +301,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)  	rdev->wiphy_idx = wiphy_counter++; -	if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { +	if (unlikely(rdev->wiphy_idx < 0)) {  		wiphy_counter--;  		mutex_unlock(&cfg80211_mutex);  		/* ugh, wrapped! */ @@ -325,10 +318,14 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)  	mutex_init(&rdev->devlist_mtx);  	mutex_init(&rdev->sched_scan_mtx);  	INIT_LIST_HEAD(&rdev->wdev_list); +	INIT_LIST_HEAD(&rdev->beacon_registrations); +	spin_lock_init(&rdev->beacon_registrations_lock);  	spin_lock_init(&rdev->bss_lock);  	INIT_LIST_HEAD(&rdev->bss_list);  	INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);  	INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); +	INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk, +			  cfg80211_dfs_channels_update_work);  #ifdef CONFIG_CFG80211_WEXT  	rdev->wiphy.wext = &cfg80211_wext_handler;  #endif @@ -370,6 +367,8 @@ 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); @@ -385,8 +384,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)  		c = &wiphy->iface_combinations[i]; -		/* Combinations with just one interface aren't real */ -		if (WARN_ON(c->max_interfaces < 2)) +		/* +		 * Combinations with just one interface aren't real, +		 * however we make an exception for DFS. +		 */ +		if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths))  			return -EINVAL;  		/* Need at least one channel */ @@ -401,6 +403,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)  				CFG80211_MAX_NUM_DIFFERENT_CHANNELS))  			return -EINVAL; +		/* DFS only works on one channel. */ +		if (WARN_ON(c->radar_detect_widths && +			    (c->num_different_channels > 1))) +			return -EINVAL; +  		if (WARN_ON(!c->n_limits))  			return -EINVAL; @@ -473,6 +480,11 @@ int wiphy_register(struct wiphy *wiphy)  			   ETH_ALEN)))  		return -EINVAL; +	if (WARN_ON(wiphy->max_acl_mac_addrs && +		    (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) || +		     !rdev->ops->set_mac_acl))) +		return -EINVAL; +  	if (wiphy->addresses)  		memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); @@ -685,9 +697,10 @@ void wiphy_unregister(struct wiphy *wiphy)  	flush_work(&rdev->scan_done_wk);  	cancel_work_sync(&rdev->conn_work);  	flush_work(&rdev->event_work); +	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);  	if (rdev->wowlan && rdev->ops->set_wakeup) -		rdev->ops->set_wakeup(&rdev->wiphy, false); +		rdev_set_wakeup(rdev, false);  	cfg80211_rdev_free_wowlan(rdev);  }  EXPORT_SYMBOL(wiphy_unregister); @@ -695,12 +708,17 @@ EXPORT_SYMBOL(wiphy_unregister);  void cfg80211_dev_free(struct cfg80211_registered_device *rdev)  {  	struct cfg80211_internal_bss *scan, *tmp; +	struct cfg80211_beacon_registration *reg, *treg;  	rfkill_destroy(rdev->rfkill);  	mutex_destroy(&rdev->mtx);  	mutex_destroy(&rdev->devlist_mtx);  	mutex_destroy(&rdev->sched_scan_mtx); +	list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) { +		list_del(®->list); +		kfree(reg); +	}  	list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) -		cfg80211_put_bss(&scan->pub); +		cfg80211_put_bss(&rdev->wiphy, &scan->pub);  	kfree(rdev);  } @@ -770,7 +788,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)  	case NL80211_IFTYPE_P2P_DEVICE:  		if (!wdev->p2p_started)  			break; -		rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); +		rdev_stop_p2p_device(rdev, wdev);  		wdev->p2p_started = false;  		rdev->opencount--;  		break; @@ -856,8 +874,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  		/* allow mac80211 to determine the timeout */  		wdev->ps_timeout = -1; -		if (!dev->ethtool_ops) -			dev->ethtool_ops = &cfg80211_ethtool_ops; +		netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops);  		if ((wdev->iftype == NL80211_IFTYPE_STATION ||  		     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || @@ -961,9 +978,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  		if ((wdev->iftype == NL80211_IFTYPE_STATION ||  		     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&  		    rdev->ops->set_power_mgmt) -			if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, -						      wdev->ps, -						      wdev->ps_timeout)) { +			if (rdev_set_power_mgmt(rdev, dev, wdev->ps, +						wdev->ps_timeout)) {  				/* assume this means it's off */  				wdev->ps = false;  			} diff --git a/net/wireless/core.h b/net/wireless/core.h index a343be4a52b..3aec0e429d8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -8,7 +8,6 @@  #include <linux/mutex.h>  #include <linux/list.h>  #include <linux/netdevice.h> -#include <linux/kref.h>  #include <linux/rbtree.h>  #include <linux/debugfs.h>  #include <linux/rfkill.h> @@ -18,6 +17,9 @@  #include <net/cfg80211.h>  #include "reg.h" + +#define WIPHY_IDX_INVALID	-1 +  struct cfg80211_registered_device {  	const struct cfg80211_ops *ops;  	struct list_head list; @@ -55,7 +57,8 @@ struct cfg80211_registered_device {  	int opencount; /* also protected by devlist_mtx */  	wait_queue_head_t dev_wait; -	u32 ap_beacons_nlportid; +	struct list_head beacon_registrations; +	spinlock_t beacon_registrations_lock;  	/* protected by RTNL only */  	int num_running_ifaces; @@ -83,9 +86,11 @@ struct cfg80211_registered_device {  	struct cfg80211_wowlan *wowlan; +	struct delayed_work dfs_update_channels_wk; +  	/* must be last because of the way we do wiphy_priv(),  	 * and it should at least be aligned to NETDEV_ALIGN */ -	struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); +	struct wiphy wiphy __aligned(NETDEV_ALIGN);  };  static inline @@ -95,13 +100,6 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)  	return container_of(wiphy, struct cfg80211_registered_device, wiphy);  } -/* Note 0 is valid, hence phy0 */ -static inline -bool wiphy_idx_valid(int wiphy_idx) -{ -	return wiphy_idx >= 0; -} -  static inline void  cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)  { @@ -112,6 +110,9 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)  	for (i = 0; i < rdev->wowlan->n_patterns; i++)  		kfree(rdev->wowlan->patterns[i].mask);  	kfree(rdev->wowlan->patterns); +	if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock) +		sock_release(rdev->wowlan->tcp->sock); +	kfree(rdev->wowlan->tcp);  	kfree(rdev->wowlan);  } @@ -125,20 +126,13 @@ static inline void assert_cfg80211_lock(void)  	lockdep_assert_held(&cfg80211_mutex);  } -/* - * You can use this to mark a wiphy_idx as not having an associated wiphy. - * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL - */ -#define WIPHY_IDX_STALE -1 -  struct cfg80211_internal_bss {  	struct list_head list; +	struct list_head hidden_list;  	struct rb_node rbn;  	unsigned long ts; -	struct kref ref; +	unsigned long refcount;  	atomic_t hold; -	bool beacon_ies_allocated; -	bool proberesp_ies_allocated;  	/* must be last because of priv member */  	struct cfg80211_bss pub; @@ -260,6 +254,10 @@ enum cfg80211_chan_mode {  	CHAN_MODE_EXCLUSIVE,  }; +struct cfg80211_beacon_registration { +	struct list_head list; +	u32 nlportid; +};  /* free object */  extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -304,9 +302,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,  		       const struct mesh_config *conf);  int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,  			struct net_device *dev); -int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, -			   struct wireless_dev *wdev, int freq, -			   enum nl80211_channel_type channel_type); +int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, +			      struct wireless_dev *wdev, +			      struct cfg80211_chan_def *chandef);  /* AP */  int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, @@ -320,13 +318,15 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  			 const u8 *bssid,  			 const u8 *ssid, int ssid_len,  			 const u8 *ie, int ie_len, -			 const u8 *key, int key_len, int key_idx); +			 const u8 *key, int key_len, int key_idx, +			 const u8 *sae_data, int sae_data_len);  int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  		       struct net_device *dev, struct ieee80211_channel *chan,  		       enum nl80211_auth_type auth_type, const u8 *bssid,  		       const u8 *ssid, int ssid_len,  		       const u8 *ie, int ie_len, -		       const u8 *key, int key_len, int key_idx); +		       const u8 *key, int key_len, int key_idx, +		       const u8 *sae_data, int sae_data_len);  int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,  			  struct net_device *dev,  			  struct ieee80211_channel *chan, @@ -371,10 +371,8 @@ 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, -			  enum nl80211_channel_type channel_type, -			  bool channel_type_valid, unsigned int wait, -			  const u8 *buf, size_t len, bool no_cck, -			  bool dont_wait_for_ack, u64 *cookie); +			  unsigned int wait, const u8 *buf, size_t len, +			  bool no_cck, bool dont_wait_for_ack, u64 *cookie);  void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,  			       const struct ieee80211_ht_cap *ht_capa_mask); @@ -432,7 +430,24 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  				 struct wireless_dev *wdev,  				 enum nl80211_iftype iftype,  				 struct ieee80211_channel *chan, -				 enum cfg80211_chan_mode chanmode); +				 enum cfg80211_chan_mode chanmode, +				 u8 radar_detect); + +/** + * cfg80211_chandef_dfs_required - checks if radar detection is required + * @wiphy: the wiphy to validate against + * @chandef: the channel definition to check + * Return: 1 if radar detection is required, 0 if it is not, < 0 on error + */ +int cfg80211_chandef_dfs_required(struct wiphy *wiphy, +				  const struct cfg80211_chan_def *c); + +void cfg80211_set_dfs_state(struct wiphy *wiphy, +			    const struct cfg80211_chan_def *chandef, +			    enum nl80211_dfs_state dfs_state); + +void cfg80211_dfs_channels_update_work(struct work_struct *work); +  static inline int  cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, @@ -440,7 +455,7 @@ cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,  			      enum nl80211_iftype iftype)  {  	return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, -					    CHAN_MODE_UNDEFINED); +					    CHAN_MODE_UNDEFINED, 0);  }  static inline int @@ -457,7 +472,17 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,  		      enum cfg80211_chan_mode chanmode)  {  	return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, -					    chan, chanmode); +					    chan, chanmode, 0); +} + +static inline unsigned int elapsed_jiffies_msecs(unsigned long start) +{ +	unsigned long end = jiffies; + +	if (end >= start) +		return jiffies_to_msecs(end - start); + +	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);  }  void @@ -465,11 +490,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,  		        struct ieee80211_channel **chan,  		        enum cfg80211_chan_mode *chanmode); -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, -		  int freq, enum nl80211_channel_type channel_type);  int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, -				 int freq, enum nl80211_channel_type chantype); +				 struct cfg80211_chan_def *chandef);  int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,  			   const u8 *rates, unsigned int n_rates, diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index 7eecdf40cf8..e37862f1b12 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -2,6 +2,7 @@  #include <net/cfg80211.h>  #include "core.h"  #include "ethtool.h" +#include "rdev-ops.h"  static void cfg80211_get_drvinfo(struct net_device *dev,  					struct ethtool_drvinfo *info) @@ -14,10 +15,10 @@ static void cfg80211_get_drvinfo(struct net_device *dev,  	strlcpy(info->version, init_utsname()->release, sizeof(info->version));  	if (wdev->wiphy->fw_version[0]) -		strncpy(info->fw_version, wdev->wiphy->fw_version, +		strlcpy(info->fw_version, wdev->wiphy->fw_version,  			sizeof(info->fw_version));  	else -		strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); +		strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));  	strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)),  		sizeof(info->bus_info)); @@ -47,9 +48,8 @@ static void cfg80211_get_ringparam(struct net_device *dev,  	memset(rp, 0, sizeof(*rp));  	if (rdev->ops->get_ringparam) -		rdev->ops->get_ringparam(wdev->wiphy, -					 &rp->tx_pending, &rp->tx_max_pending, -					 &rp->rx_pending, &rp->rx_max_pending); +		rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending, +				   &rp->rx_pending, &rp->rx_max_pending);  }  static int cfg80211_set_ringparam(struct net_device *dev, @@ -62,8 +62,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,  		return -EINVAL;  	if (rdev->ops->set_ringparam) -		return rdev->ops->set_ringparam(wdev->wiphy, -						rp->tx_pending, rp->rx_pending); +		return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending);  	return -ENOTSUPP;  } @@ -73,7 +72,7 @@ static int cfg80211_get_sset_count(struct net_device *dev, int sset)  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);  	if (rdev->ops->get_et_sset_count) -		return rdev->ops->get_et_sset_count(wdev->wiphy, dev, sset); +		return rdev_get_et_sset_count(rdev, dev, sset);  	return -EOPNOTSUPP;  } @@ -83,7 +82,7 @@ static void cfg80211_get_stats(struct net_device *dev,  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);  	if (rdev->ops->get_et_stats) -		rdev->ops->get_et_stats(wdev->wiphy, dev, stats, data); +		rdev_get_et_stats(rdev, dev, stats, data);  }  static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data) @@ -91,7 +90,7 @@ static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);  	if (rdev->ops->get_et_strings) -		rdev->ops->get_et_strings(wdev->wiphy, dev, sset, data); +		rdev_get_et_strings(rdev, dev, sset, data);  }  const struct ethtool_ops cfg80211_ethtool_ops = { diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index ca5672f6ee2..d80e47194d4 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -11,6 +11,7 @@  #include <net/cfg80211.h>  #include "wext-compat.h"  #include "nl80211.h" +#include "rdev-ops.h"  void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) @@ -36,7 +37,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)  	if (wdev->current_bss) {  		cfg80211_unhold_bss(wdev->current_bss); -		cfg80211_put_bss(&wdev->current_bss->pub); +		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);  	}  	cfg80211_hold_bss(bss_from_pub(bss)); @@ -61,6 +62,8 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)  	struct cfg80211_event *ev;  	unsigned long flags; +	trace_cfg80211_ibss_joined(dev, bssid); +  	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);  	ev = kzalloc(sizeof(*ev), gfp); @@ -97,9 +100,9 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  		* 11a for maximum compatibility.  		*/  		struct ieee80211_supported_band *sband = -			rdev->wiphy.bands[params->channel->band]; +			rdev->wiphy.bands[params->chandef.chan->band];  		int j; -		u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ? +		u32 flag = params->chandef.chan->band == IEEE80211_BAND_5GHZ ?  			IEEE80211_RATE_MANDATORY_A :  			IEEE80211_RATE_MANDATORY_B; @@ -115,11 +118,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  	wdev->ibss_fixed = params->channel_fixed;  #ifdef CONFIG_CFG80211_WEXT -	wdev->wext.ibss.channel = params->channel; +	wdev->wext.ibss.chandef = params->chandef;  #endif  	wdev->sme_state = CFG80211_SME_CONNECTING; -	err = cfg80211_can_use_chan(rdev, wdev, params->channel, +	err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,  				    params->channel_fixed  				    ? CHAN_MODE_SHARED  				    : CHAN_MODE_EXCLUSIVE); @@ -128,7 +131,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  		return err;  	} -	err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); +	err = rdev_join_ibss(rdev, dev, params);  	if (err) {  		wdev->connect_keys = NULL;  		wdev->sme_state = CFG80211_SME_IDLE; @@ -175,11 +178,11 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)  	 */  	if (rdev->ops->del_key)  		for (i = 0; i < 6; i++) -			rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); +			rdev_del_key(rdev, dev, i, false, NULL);  	if (wdev->current_bss) {  		cfg80211_unhold_bss(wdev->current_bss); -		cfg80211_put_bss(&wdev->current_bss->pub); +		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);  	}  	wdev->current_bss = NULL; @@ -211,7 +214,7 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,  	if (!wdev->ssid_len)  		return -ENOLINK; -	err = rdev->ops->leave_ibss(&rdev->wiphy, dev); +	err = rdev_leave_ibss(rdev, dev);  	if (err)  		return err; @@ -248,7 +251,9 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,  		wdev->wext.ibss.beacon_interval = 100;  	/* try to find an IBSS channel if none requested ... */ -	if (!wdev->wext.ibss.channel) { +	if (!wdev->wext.ibss.chandef.chan) { +		wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +  		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {  			struct ieee80211_supported_band *sband;  			struct ieee80211_channel *chan; @@ -263,15 +268,15 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,  					continue;  				if (chan->flags & IEEE80211_CHAN_DISABLED)  					continue; -				wdev->wext.ibss.channel = chan; +				wdev->wext.ibss.chandef.chan = chan;  				break;  			} -			if (wdev->wext.ibss.channel) +			if (wdev->wext.ibss.chandef.chan)  				break;  		} -		if (!wdev->wext.ibss.channel) +		if (!wdev->wext.ibss.chandef.chan)  			return -EINVAL;  	} @@ -333,7 +338,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,  			return -EINVAL;  	} -	if (wdev->wext.ibss.channel == chan) +	if (wdev->wext.ibss.chandef.chan == chan)  		return 0;  	wdev_lock(wdev); @@ -346,7 +351,8 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,  		return err;  	if (chan) { -		wdev->wext.ibss.channel = chan; +		wdev->wext.ibss.chandef.chan = chan; +		wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;  		wdev->wext.ibss.channel_fixed = true;  	} else {  		/* cfg80211_ibss_wext_join will pick one if needed */ @@ -376,8 +382,8 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev,  	wdev_lock(wdev);  	if (wdev->current_bss)  		chan = wdev->current_bss->pub.channel; -	else if (wdev->wext.ibss.channel) -		chan = wdev->wext.ibss.channel; +	else if (wdev->wext.ibss.chandef.chan) +		chan = wdev->wext.ibss.chandef.chan;  	wdev_unlock(wdev);  	if (chan) { diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index c384e77ff77..55957a284f6 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -3,6 +3,7 @@  #include <net/cfg80211.h>  #include "nl80211.h"  #include "core.h" +#include "rdev-ops.h"  /* Default values, timeouts in ms */  #define MESH_TTL 		31 @@ -43,6 +44,10 @@  #define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50 +#define MESH_DEFAULT_BEACON_INTERVAL	1000	/* in 1024 us units (=TUs) */ +#define MESH_DEFAULT_DTIM_PERIOD	2 +#define MESH_DEFAULT_AWAKE_WINDOW	10	/* in 1024 us units (=TUs) */ +  const struct mesh_config default_mesh_config = {  	.dot11MeshRetryTimeout = MESH_RET_T,  	.dot11MeshConfirmTimeout = MESH_CONF_T, @@ -68,18 +73,20 @@ const struct mesh_config default_mesh_config = {  	.dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT,  	.dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,  	.dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, +	.power_mode = NL80211_MESH_POWER_ACTIVE, +	.dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,  };  const struct mesh_setup default_mesh_setup = {  	/* cfg80211_join_mesh() will pick a channel if needed */ -	.channel = NULL, -	.channel_type = NL80211_CHAN_NO_HT,  	.sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,  	.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,  	.path_metric = IEEE80211_PATH_METRIC_AIRTIME,  	.ie = NULL,  	.ie_len = 0,  	.is_secure = false, +	.beacon_interval = MESH_DEFAULT_BEACON_INTERVAL, +	.dtim_period = MESH_DEFAULT_DTIM_PERIOD,  };  int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, @@ -110,13 +117,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,  	if (!rdev->ops->join_mesh)  		return -EOPNOTSUPP; -	if (!setup->channel) { +	if (!setup->chandef.chan) {  		/* if no channel explicitly given, use preset channel */ -		setup->channel = wdev->preset_chan; -		setup->channel_type = wdev->preset_chantype; +		setup->chandef = wdev->preset_chandef;  	} -	if (!setup->channel) { +	if (!setup->chandef.chan) {  		/* if we don't have that either, use the first usable channel */  		enum ieee80211_band band; @@ -136,35 +142,35 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,  						   IEEE80211_CHAN_DISABLED |  						   IEEE80211_CHAN_RADAR))  					continue; -				setup->channel = chan; +				setup->chandef.chan = chan;  				break;  			} -			if (setup->channel) +			if (setup->chandef.chan)  				break;  		}  		/* no usable channel ... */ -		if (!setup->channel) +		if (!setup->chandef.chan)  			return -EINVAL; -		setup->channel_type = NL80211_CHAN_NO_HT; +		setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +		setup->chandef.center_freq1 = setup->chandef.chan->center_freq;  	} -	if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel, -					  setup->channel_type)) +	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))  		return -EINVAL; -	err = cfg80211_can_use_chan(rdev, wdev, setup->channel, +	err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan,  				    CHAN_MODE_SHARED);  	if (err)  		return err; -	err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); +	err = rdev_join_mesh(rdev, dev, conf, setup);  	if (!err) {  		memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);  		wdev->mesh_id_len = setup->mesh_id_len; -		wdev->channel = setup->channel; +		wdev->channel = setup->chandef.chan;  	}  	return err; @@ -187,20 +193,12 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,  	return err;  } -int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, -			   struct wireless_dev *wdev, int freq, -			   enum nl80211_channel_type channel_type) +int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, +			      struct wireless_dev *wdev, +			      struct cfg80211_chan_def *chandef)  { -	struct ieee80211_channel *channel;  	int err; -	channel = rdev_freq_to_chan(rdev, freq, channel_type); -	if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, -						      channel, -						      channel_type)) { -		return -EINVAL; -	} -  	/*  	 * Workaround for libertas (only!), it puts the interface  	 * into mesh mode but doesn't implement join_mesh. Instead, @@ -209,22 +207,21 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,  	 * compatible with 802.11 mesh.  	 */  	if (rdev->ops->libertas_set_mesh_channel) { -		if (channel_type != NL80211_CHAN_NO_HT) +		if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)  			return -EINVAL;  		if (!netif_running(wdev->netdev))  			return -ENETDOWN; -		err = cfg80211_can_use_chan(rdev, wdev, channel, +		err = cfg80211_can_use_chan(rdev, wdev, chandef->chan,  					    CHAN_MODE_SHARED);  		if (err)  			return err; -		err = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, -							   wdev->netdev, -							   channel); +		err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev, +						     chandef->chan);  		if (!err) -			wdev->channel = channel; +			wdev->channel = chandef->chan;  		return err;  	} @@ -232,8 +229,7 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,  	if (wdev->mesh_id_len)  		return -EBUSY; -	wdev->preset_chan = channel; -	wdev->preset_chantype = channel_type; +	wdev->preset_chandef = *chandef;  	return 0;  } @@ -242,6 +238,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; +	trace_cfg80211_notify_new_peer_candidate(dev, macaddr);  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))  		return; @@ -267,7 +264,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,  	if (!wdev->mesh_id_len)  		return -ENOTCONN; -	err = rdev->ops->leave_mesh(&rdev->wiphy, dev); +	err = rdev_leave_mesh(rdev, dev);  	if (!err) {  		wdev->mesh_id_len = 0;  		wdev->channel = NULL; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 904a7f36832..caddca35d68 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -15,6 +15,8 @@  #include <net/iw_handler.h>  #include "core.h"  #include "nl80211.h" +#include "rdev-ops.h" +  void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)  { @@ -22,6 +24,7 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_send_rx_auth(dev);  	wdev_lock(wdev);  	nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); @@ -42,6 +45,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,  	u8 *ie = mgmt->u.assoc_resp.variable;  	int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); +	trace_cfg80211_send_rx_assoc(dev, bss);  	wdev_lock(wdev);  	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); @@ -54,7 +58,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,  	 */  	if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&  	    cfg80211_sme_failed_reassoc(wdev)) { -		cfg80211_put_bss(bss); +		cfg80211_put_bss(wiphy, bss);  		goto out;  	} @@ -66,7 +70,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,  		 * do not call connect_result() now because the  		 * sme will schedule work that does it later.  		 */ -		cfg80211_put_bss(bss); +		cfg80211_put_bss(wiphy, bss);  		goto out;  	} @@ -98,12 +102,13 @@ void __cfg80211_send_deauth(struct net_device *dev,  	const u8 *bssid = mgmt->bssid;  	bool was_current = false; +	trace___cfg80211_send_deauth(dev);  	ASSERT_WDEV_LOCK(wdev);  	if (wdev->current_bss &&  	    ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {  		cfg80211_unhold_bss(wdev->current_bss); -		cfg80211_put_bss(&wdev->current_bss->pub); +		cfg80211_put_bss(wiphy, &wdev->current_bss->pub);  		wdev->current_bss = NULL;  		was_current = true;  	} @@ -147,6 +152,7 @@ void __cfg80211_send_disassoc(struct net_device *dev,  	u16 reason_code;  	bool from_ap; +	trace___cfg80211_send_disassoc(dev);  	ASSERT_WDEV_LOCK(wdev);  	nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); @@ -158,7 +164,7 @@ void __cfg80211_send_disassoc(struct net_device *dev,  	    ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {  		cfg80211_sme_disassoc(dev, wdev->current_bss);  		cfg80211_unhold_bss(wdev->current_bss); -		cfg80211_put_bss(&wdev->current_bss->pub); +		cfg80211_put_bss(wiphy, &wdev->current_bss->pub);  		wdev->current_bss = NULL;  	} else  		WARN_ON(1); @@ -188,6 +194,7 @@ void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_send_unprot_deauth(dev);  	nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC);  }  EXPORT_SYMBOL(cfg80211_send_unprot_deauth); @@ -199,6 +206,7 @@ void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_send_unprot_disassoc(dev);  	nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC);  }  EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); @@ -209,6 +217,7 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_send_auth_timeout(dev, addr);  	wdev_lock(wdev);  	nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); @@ -227,6 +236,7 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_send_assoc_timeout(dev, addr);  	wdev_lock(wdev);  	nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); @@ -261,6 +271,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,  	}  #endif +	trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc);  	nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);  }  EXPORT_SYMBOL(cfg80211_michael_mic_failure); @@ -273,7 +284,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  			 const u8 *bssid,  			 const u8 *ssid, int ssid_len,  			 const u8 *ie, int ie_len, -			 const u8 *key, int key_len, int key_idx) +			 const u8 *key, int key_len, int key_idx, +			 const u8 *sae_data, int sae_data_len)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_auth_request req; @@ -293,6 +305,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  	req.ie = ie;  	req.ie_len = ie_len; +	req.sae_data = sae_data; +	req.sae_data_len = sae_data_len;  	req.auth_type = auth_type;  	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,  				   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -307,10 +321,10 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  	if (err)  		goto out; -	err = rdev->ops->auth(&rdev->wiphy, dev, &req); +	err = rdev_auth(rdev, dev, &req);  out: -	cfg80211_put_bss(req.bss); +	cfg80211_put_bss(&rdev->wiphy, req.bss);  	return err;  } @@ -319,7 +333,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  		       enum nl80211_auth_type auth_type, const u8 *bssid,  		       const u8 *ssid, int ssid_len,  		       const u8 *ie, int ie_len, -		       const u8 *key, int key_len, int key_idx) +		       const u8 *key, int key_len, int key_idx, +		       const u8 *sae_data, int sae_data_len)  {  	int err; @@ -327,7 +342,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  	wdev_lock(dev->ieee80211_ptr);  	err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,  				   ssid, ssid_len, ie, ie_len, -				   key, key_len, key_idx); +				   key, key_len, key_idx, +				   sae_data, sae_data_len);  	wdev_unlock(dev->ieee80211_ptr);  	mutex_unlock(&rdev->devlist_mtx); @@ -410,13 +426,13 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,  	if (err)  		goto out; -	err = rdev->ops->assoc(&rdev->wiphy, dev, &req); +	err = rdev_assoc(rdev, dev, &req);  out:  	if (err) {  		if (was_connected)  			wdev->sme_state = CFG80211_SME_CONNECTED; -		cfg80211_put_bss(req.bss); +		cfg80211_put_bss(&rdev->wiphy, req.bss);  	}  	return err; @@ -466,7 +482,7 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,  	    !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))  		return 0; -	return rdev->ops->deauth(&rdev->wiphy, dev, &req); +	return rdev_deauth(rdev, dev, &req);  }  int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, @@ -498,7 +514,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,  	if (wdev->sme_state != CFG80211_SME_CONNECTED)  		return -ENOTCONN; -	if (WARN_ON(!wdev->current_bss)) +	if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))  		return -ENOTCONN;  	memset(&req, 0, sizeof(req)); @@ -511,7 +527,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,  	else  		return -ENOTCONN; -	return rdev->ops->disassoc(&rdev->wiphy, dev, &req); +	return rdev_disassoc(rdev, dev, &req);  }  int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, @@ -552,38 +568,36 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,  	memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);  	req.bssid = bssid; -	rdev->ops->deauth(&rdev->wiphy, dev, &req); +	rdev_deauth(rdev, dev, &req);  	if (wdev->current_bss) {  		cfg80211_unhold_bss(wdev->current_bss); -		cfg80211_put_bss(&wdev->current_bss->pub); +		cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);  		wdev->current_bss = NULL;  	}  }  void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,  			       struct ieee80211_channel *chan, -			       enum nl80211_channel_type channel_type,  			       unsigned int duration, gfp_t gfp)  {  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); -	nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type, -				       duration, gfp); +	trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration); +	nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, duration, gfp);  }  EXPORT_SYMBOL(cfg80211_ready_on_channel);  void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,  					struct ieee80211_channel *chan, -					enum nl80211_channel_type channel_type,  					gfp_t gfp)  {  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); -	nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, -					      channel_type, gfp); +	trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan); +	nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, gfp);  }  EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); @@ -593,6 +607,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,  	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_new_sta(dev, mac_addr, sinfo);  	nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);  }  EXPORT_SYMBOL(cfg80211_new_sta); @@ -602,6 +617,7 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)  	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_del_sta(dev, mac_addr);  	nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp);  }  EXPORT_SYMBOL(cfg80211_del_sta); @@ -682,7 +698,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,  	list_add(&nreg->list, &wdev->mgmt_registrations);  	if (rdev->ops->mgmt_frame_register) -		rdev->ops->mgmt_frame_register(wiphy, wdev, frame_type, true); +		rdev_mgmt_frame_register(rdev, wdev, frame_type, true);   out:  	spin_unlock_bh(&wdev->mgmt_registrations_lock); @@ -705,8 +721,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)  		if (rdev->ops->mgmt_frame_register) {  			u16 frame_type = le16_to_cpu(reg->frame_type); -			rdev->ops->mgmt_frame_register(wiphy, wdev, -						       frame_type, false); +			rdev_mgmt_frame_register(rdev, wdev, +						 frame_type, false);  		}  		list_del(®->list); @@ -736,10 +752,8 @@ 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, -			  enum nl80211_channel_type channel_type, -			  bool channel_type_valid, unsigned int wait, -			  const u8 *buf, size_t len, bool no_cck, -			  bool dont_wait_for_ack, u64 *cookie) +			  unsigned int wait, const u8 *buf, size_t len, +			  bool no_cck, bool dont_wait_for_ack, u64 *cookie)  {  	const struct ieee80211_mgmt *mgmt;  	u16 stype; @@ -832,10 +846,9 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,  		return -EINVAL;  	/* Transmit the Action frame as requested by user space */ -	return rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, -				  channel_type, channel_type_valid, -				  wait, buf, len, no_cck, dont_wait_for_ack, -				  cookie); +	return rdev_mgmt_tx(rdev, wdev, chan, offchan, +			    wait, buf, len, no_cck, dont_wait_for_ack, +			    cookie);  }  bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, @@ -854,10 +867,13 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,  		cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);  	u16 stype; +	trace_cfg80211_rx_mgmt(wdev, freq, sig_mbm);  	stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; -	if (!(stypes->rx & BIT(stype))) +	if (!(stypes->rx & BIT(stype))) { +		trace_cfg80211_return_bool(false);  		return false; +	}  	data = buf + ieee80211_hdrlen(mgmt->frame_control);  	data_len = len - ieee80211_hdrlen(mgmt->frame_control); @@ -888,6 +904,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,  	spin_unlock_bh(&wdev->mgmt_registrations_lock); +	trace_cfg80211_return_bool(result);  	return result;  }  EXPORT_SYMBOL(cfg80211_rx_mgmt); @@ -898,6 +915,8 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_mgmt_tx_status(wdev, cookie, ack); +  	/* Indicate TX status of the Action frame to user space */  	nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp);  } @@ -911,6 +930,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_cqm_rssi_notify(dev, rssi_event); +  	/* Indicate roaming trigger event to user space */  	nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);  } @@ -923,6 +944,8 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets); +  	/* Indicate roaming trigger event to user space */  	nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);  } @@ -948,6 +971,7 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_gtk_rekey_notify(dev, bssid);  	nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);  }  EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); @@ -959,59 +983,127 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,  	struct wiphy *wiphy = wdev->wiphy;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);  	nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);  }  EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); -void cfg80211_ch_switch_notify(struct net_device *dev, int freq, -			       enum nl80211_channel_type type) +void cfg80211_dfs_channels_update_work(struct work_struct *work)  { -	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); -	struct ieee80211_channel *chan; +	struct delayed_work *delayed_work; +	struct cfg80211_registered_device *rdev; +	struct cfg80211_chan_def chandef; +	struct ieee80211_supported_band *sband; +	struct ieee80211_channel *c; +	struct wiphy *wiphy; +	bool check_again = false; +	unsigned long timeout, next_time = 0; +	int bandid, i; -	wdev_lock(wdev); +	delayed_work = container_of(work, struct delayed_work, work); +	rdev = container_of(delayed_work, struct cfg80211_registered_device, +			    dfs_update_channels_wk); +	wiphy = &rdev->wiphy; -	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && -		    wdev->iftype != NL80211_IFTYPE_P2P_GO)) -		goto out; +	mutex_lock(&cfg80211_mutex); +	for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) { +		sband = wiphy->bands[bandid]; +		if (!sband) +			continue; -	chan = rdev_freq_to_chan(rdev, freq, type); -	if (WARN_ON(!chan)) -		goto out; +		for (i = 0; i < sband->n_channels; i++) { +			c = &sband->channels[i]; -	wdev->channel = chan; -	nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); -out: -	wdev_unlock(wdev); -	return; +			if (c->dfs_state != NL80211_DFS_UNAVAILABLE) +				continue; + +			timeout = c->dfs_state_entered + +				  IEEE80211_DFS_MIN_NOP_TIME_MS; + +			if (time_after_eq(jiffies, timeout)) { +				c->dfs_state = NL80211_DFS_USABLE; +				cfg80211_chandef_create(&chandef, c, +							NL80211_CHAN_NO_HT); + +				nl80211_radar_notify(rdev, &chandef, +						     NL80211_RADAR_NOP_FINISHED, +						     NULL, GFP_ATOMIC); +				continue; +			} + +			if (!check_again) +				next_time = timeout - jiffies; +			else +				next_time = min(next_time, timeout - jiffies); +			check_again = true; +		} +	} +	mutex_unlock(&cfg80211_mutex); + +	/* reschedule if there are other channels waiting to be cleared again */ +	if (check_again) +		queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, +				   next_time);  } -EXPORT_SYMBOL(cfg80211_ch_switch_notify); -bool cfg80211_rx_spurious_frame(struct net_device *dev, -				const u8 *addr, gfp_t gfp) + +void cfg80211_radar_event(struct wiphy *wiphy, +			  struct cfg80211_chan_def *chandef, +			  gfp_t gfp)  { -	struct wireless_dev *wdev = dev->ieee80211_ptr; +	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	unsigned long timeout; -	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && -		    wdev->iftype != NL80211_IFTYPE_P2P_GO)) -		return false; +	trace_cfg80211_radar_event(wiphy, chandef); + +	/* only set the chandef supplied channel to unavailable, in +	 * case the radar is detected on only one of multiple channels +	 * spanned by the chandef. +	 */ +	cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); -	return nl80211_unexpected_frame(dev, addr, gfp); +	timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS); +	queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, +			   timeout); + +	nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);  } -EXPORT_SYMBOL(cfg80211_rx_spurious_frame); +EXPORT_SYMBOL(cfg80211_radar_event); -bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, -					const u8 *addr, gfp_t gfp) +void cfg80211_cac_event(struct net_device *netdev, +			enum nl80211_radar_event event, gfp_t gfp)  { -	struct wireless_dev *wdev = dev->ieee80211_ptr; +	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; -	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && -		    wdev->iftype != NL80211_IFTYPE_P2P_GO && -		    wdev->iftype != NL80211_IFTYPE_AP_VLAN)) -		return false; +	trace_cfg80211_cac_event(netdev, event); + +	if (WARN_ON(!wdev->cac_started)) +		return; + +	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); +		break; +	case NL80211_RADAR_CAC_ABORTED: +		break; +	default: +		WARN_ON(1); +		return; +	} +	wdev->cac_started = false; -	return nl80211_unexpected_4addr_frame(dev, addr, gfp); +	nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);  } -EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); +EXPORT_SYMBOL(cfg80211_cac_event); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0418a6d5c1a..d44ab216c0e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -19,11 +19,12 @@  #include <net/genetlink.h>  #include <net/cfg80211.h>  #include <net/sock.h> +#include <net/inet_connection_sock.h>  #include "core.h"  #include "nl80211.h"  #include "reg.h" +#include "rdev-ops.h" -static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type);  static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,  				   struct genl_info *info,  				   struct cfg80211_crypto_settings *settings, @@ -223,8 +224,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {  	[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,  				      .len = 20-1 },  	[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, +  	[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },  	[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, +	[NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 }, +	[NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 }, +	[NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 }, +  	[NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },  	[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },  	[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, @@ -355,6 +361,15 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {  	[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },  	[NL80211_ATTR_WDEV] = { .type = NLA_U64 },  	[NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, +	[NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, }, +	[NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN }, +	[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, +	[NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, +	[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, +	[NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, +	[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, +	[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, +	[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },  };  /* policy for the key attributes */ @@ -387,6 +402,26 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {  	[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },  	[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },  	[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, +	[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, +}; + +static const struct nla_policy +nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { +	[NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 }, +	[NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 }, +	[NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN }, +	[NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 }, +	[NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 }, +	[NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 }, +	[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = { +		.len = sizeof(struct nl80211_wowlan_tcp_data_seq) +	}, +	[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = { +		.len = sizeof(struct nl80211_wowlan_tcp_data_token) +	}, +	[NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 }, +	[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 }, +	[NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },  };  /* policy for GTK rekey offload attributes */ @@ -690,7 +725,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)  static struct cfg80211_cached_keys *  nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, -		       struct nlattr *keys) +		       struct nlattr *keys, bool *no_ht)  {  	struct key_parse parse;  	struct nlattr *key; @@ -733,6 +768,12 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,  		result->params[parse.idx].key_len = parse.p.key_len;  		result->params[parse.idx].key = result->data[parse.idx];  		memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); + +		if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 || +		    parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) { +			if (no_ht) +				*no_ht = true; +		}  	}  	return result; @@ -943,7 +984,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag  	     dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {  		u32 tx_ant = 0, rx_ant = 0;  		int res; -		res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant); +		res = rdev_get_antenna(dev, &tx_ant, &rx_ant);  		if (!res) {  			if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,  					tx_ant) || @@ -1101,6 +1142,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag  			goto nla_put_failure;  	}  	CMD(start_p2p_device, START_P2P_DEVICE); +	CMD(set_mcast_rate, SET_MCAST_RATE);  #ifdef CONFIG_NL80211_TESTMODE  	CMD(testmode_cmd, TESTMODE); @@ -1216,6 +1258,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag  					dev->wiphy.wowlan.pattern_min_len,  				.max_pattern_len =  					dev->wiphy.wowlan.pattern_max_len, +				.max_pkt_offset = +					dev->wiphy.wowlan.max_pkt_offset,  			};  			if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,  				    sizeof(pat), &pat)) @@ -1248,6 +1292,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag  		    dev->wiphy.ht_capa_mod_mask))  		goto nla_put_failure; +	if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && +	    dev->wiphy.max_acl_mac_addrs && +	    nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX, +			dev->wiphy.max_acl_mac_addrs)) +		goto nla_put_failure; +  	return genlmsg_end(msg, hdr);   nla_put_failure: @@ -1257,7 +1307,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag  static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)  { -	int idx = 0; +	int idx = 0, ret;  	int start = cb->args[0];  	struct cfg80211_registered_device *dev; @@ -1267,9 +1317,29 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)  			continue;  		if (++idx <= start)  			continue; -		if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid, -				       cb->nlh->nlmsg_seq, NLM_F_MULTI, -				       dev) < 0) { +		ret = nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid, +					 cb->nlh->nlmsg_seq, NLM_F_MULTI, +					 dev); +		if (ret < 0) { +			/* +			 * If sending the wiphy data didn't fit (ENOBUFS or +			 * EMSGSIZE returned), this SKB is still empty (so +			 * it's not too big because another wiphy dataset is +			 * already in the skb) and we've not tried to adjust +			 * the dump allocation yet ... then adjust the alloc +			 * size to be bigger, and return 1 but with the empty +			 * skb. This results in an empty message being RX'ed +			 * in userspace, but that is ignored. +			 * +			 * We can then retry with the larger buffer. +			 */ +			if ((ret == -ENOBUFS || ret == -EMSGSIZE) && +			    !skb->len && +			    cb->min_dump_alloc < 4096) { +				cb->min_dump_alloc = 4096; +				mutex_unlock(&cfg80211_mutex); +				return 1; +			}  			idx--;  			break;  		} @@ -1286,7 +1356,7 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)  	struct sk_buff *msg;  	struct cfg80211_registered_device *dev = info->user_ptr[0]; -	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	msg = nlmsg_new(4096, GFP_KERNEL);  	if (!msg)  		return -ENOMEM; @@ -1350,51 +1420,83 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)  		wdev->iftype == NL80211_IFTYPE_P2P_GO;  } -static bool nl80211_valid_channel_type(struct genl_info *info, -				       enum nl80211_channel_type *channel_type) +static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, +				 struct genl_info *info, +				 struct cfg80211_chan_def *chandef)  { -	enum nl80211_channel_type tmp; +	u32 control_freq; -	if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) -		return false; +	if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) +		return -EINVAL; -	tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); -	if (tmp != NL80211_CHAN_NO_HT && -	    tmp != NL80211_CHAN_HT20 && -	    tmp != NL80211_CHAN_HT40PLUS && -	    tmp != NL80211_CHAN_HT40MINUS) -		return false; +	control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); -	if (channel_type) -		*channel_type = tmp; +	chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq); +	chandef->width = NL80211_CHAN_WIDTH_20_NOHT; +	chandef->center_freq1 = control_freq; +	chandef->center_freq2 = 0; -	return true; +	/* Primary channel not allowed */ +	if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED) +		return -EINVAL; + +	if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { +		enum nl80211_channel_type chantype; + +		chantype = nla_get_u32( +				info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + +		switch (chantype) { +		case NL80211_CHAN_NO_HT: +		case NL80211_CHAN_HT20: +		case NL80211_CHAN_HT40PLUS: +		case NL80211_CHAN_HT40MINUS: +			cfg80211_chandef_create(chandef, chandef->chan, +						chantype); +			break; +		default: +			return -EINVAL; +		} +	} else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) { +		chandef->width = +			nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]); +		if (info->attrs[NL80211_ATTR_CENTER_FREQ1]) +			chandef->center_freq1 = +				nla_get_u32( +					info->attrs[NL80211_ATTR_CENTER_FREQ1]); +		if (info->attrs[NL80211_ATTR_CENTER_FREQ2]) +			chandef->center_freq2 = +				nla_get_u32( +					info->attrs[NL80211_ATTR_CENTER_FREQ2]); +	} + +	if (!cfg80211_chandef_valid(chandef)) +		return -EINVAL; + +	if (!cfg80211_chandef_usable(&rdev->wiphy, chandef, +				     IEEE80211_CHAN_DISABLED)) +		return -EINVAL; + +	return 0;  }  static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,  				 struct wireless_dev *wdev,  				 struct genl_info *info)  { -	struct ieee80211_channel *channel; -	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; -	u32 freq; +	struct cfg80211_chan_def chandef;  	int result;  	enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;  	if (wdev)  		iftype = wdev->iftype; -	if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) -		return -EINVAL; -  	if (!nl80211_can_set_dev_channel(wdev))  		return -EOPNOTSUPP; -	if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && -	    !nl80211_valid_channel_type(info, &channel_type)) -		return -EINVAL; - -	freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); +	result = nl80211_parse_chandef(rdev, info, &chandef); +	if (result) +		return result;  	mutex_lock(&rdev->devlist_mtx);  	switch (iftype) { @@ -1404,22 +1506,18 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,  			result = -EBUSY;  			break;  		} -		channel = rdev_freq_to_chan(rdev, freq, channel_type); -		if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, -							      channel, -							      channel_type)) { +		if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {  			result = -EINVAL;  			break;  		} -		wdev->preset_chan = channel; -		wdev->preset_chantype = channel_type; +		wdev->preset_chandef = chandef;  		result = 0;  		break;  	case NL80211_IFTYPE_MESH_POINT: -		result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); +		result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);  		break;  	case NL80211_IFTYPE_MONITOR: -		result = cfg80211_set_monitor_channel(rdev, freq, channel_type); +		result = cfg80211_set_monitor_channel(rdev, &chandef);  		break;  	default:  		result = -EINVAL; @@ -1457,7 +1555,7 @@ static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)  		return -EOPNOTSUPP;  	bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); -	return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid); +	return rdev_set_wds_peer(rdev, dev, bssid);  } @@ -1507,10 +1605,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		result = 0;  		mutex_lock(&rdev->mtx); -	} else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) +	} else  		wdev = netdev->ieee80211_ptr; -	else -		wdev = NULL;  	/*  	 * end workaround code, by now the rdev is available @@ -1562,24 +1658,29 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  			if (result)  				goto bad_res; -			result = rdev->ops->set_txq_params(&rdev->wiphy, -							   netdev, -							   &txq_params); +			result = rdev_set_txq_params(rdev, netdev, +						     &txq_params);  			if (result)  				goto bad_res;  		}  	}  	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { -		result = __nl80211_set_channel(rdev, wdev, info); +		result = __nl80211_set_channel(rdev, +				nl80211_can_set_dev_channel(wdev) ? wdev : NULL, +				info);  		if (result)  			goto bad_res;  	}  	if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { +		struct wireless_dev *txp_wdev = wdev;  		enum nl80211_tx_power_setting type;  		int idx, mbm = 0; +		if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER)) +			txp_wdev = NULL; +  		if (!rdev->ops->set_tx_power) {  			result = -EOPNOTSUPP;  			goto bad_res; @@ -1599,7 +1700,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  			mbm = nla_get_u32(info->attrs[idx]);  		} -		result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); +		result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);  		if (result)  			goto bad_res;  	} @@ -1628,7 +1729,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;  		rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; -		result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); +		result = rdev_set_antenna(rdev, tx_ant, rx_ant);  		if (result)  			goto bad_res;  	} @@ -1713,7 +1814,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		if (changed & WIPHY_PARAM_COVERAGE_CLASS)  			rdev->wiphy.coverage_class = coverage_class; -		result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); +		result = rdev_set_wiphy_params(rdev, changed);  		if (result) {  			rdev->wiphy.retry_short = old_retry_short;  			rdev->wiphy.retry_long = old_retry_long; @@ -1736,6 +1837,35 @@ static inline u64 wdev_id(struct wireless_dev *wdev)  	       ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);  } +static int nl80211_send_chandef(struct sk_buff *msg, +				 struct cfg80211_chan_def *chandef) +{ +	WARN_ON(!cfg80211_chandef_valid(chandef)); + +	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, +			chandef->chan->center_freq)) +		return -ENOBUFS; +	switch (chandef->width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +	case NL80211_CHAN_WIDTH_40: +		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, +				cfg80211_get_chandef_type(chandef))) +			return -ENOBUFS; +		break; +	default: +		break; +	} +	if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width)) +		return -ENOBUFS; +	if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1)) +		return -ENOBUFS; +	if (chandef->center_freq2 && +	    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2)) +		return -ENOBUFS; +	return 0; +} +  static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,  			      struct cfg80211_registered_device *rdev,  			      struct wireless_dev *wdev) @@ -1762,16 +1892,18 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag  		goto nla_put_failure;  	if (rdev->ops->get_channel) { -		struct ieee80211_channel *chan; -		enum nl80211_channel_type channel_type; +		int ret; +		struct cfg80211_chan_def chandef; + +		ret = rdev_get_channel(rdev, wdev, &chandef); +		if (ret == 0) { +			if (nl80211_send_chandef(msg, &chandef)) +				goto nla_put_failure; +		} +	} -		chan = rdev->ops->get_channel(&rdev->wiphy, wdev, -					      &channel_type); -		if (chan && -		    (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, -				 chan->center_freq) || -		     nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, -				 channel_type))) +	if (wdev->ssid_len) { +		if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))  			goto nla_put_failure;  	} @@ -2000,6 +2132,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)  	    !(rdev->wiphy.interface_modes & (1 << type)))  		return -EOPNOTSUPP; +	if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) { +		nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC], +			   ETH_ALEN); +		if (!is_valid_ether_addr(params.macaddr)) +			return -EADDRNOTAVAIL; +	} +  	if (info->attrs[NL80211_ATTR_4ADDR]) {  		params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);  		err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); @@ -2014,9 +2153,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)  	err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?  				  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,  				  &flags); -	wdev = rdev->ops->add_virtual_intf(&rdev->wiphy, -		nla_data(info->attrs[NL80211_ATTR_IFNAME]), -		type, err ? NULL : &flags, ¶ms); +	wdev = rdev_add_virtual_intf(rdev, +				nla_data(info->attrs[NL80211_ATTR_IFNAME]), +				type, err ? NULL : &flags, ¶ms);  	if (IS_ERR(wdev)) {  		nlmsg_free(msg);  		return PTR_ERR(wdev); @@ -2083,7 +2222,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)  	if (!wdev->netdev)  		info->user_ptr[1] = NULL; -	return rdev->ops->del_virtual_intf(&rdev->wiphy, wdev); +	return rdev_del_virtual_intf(rdev, wdev);  }  static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) @@ -2100,7 +2239,7 @@ static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)  	noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]); -	return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map); +	return rdev_set_noack_map(rdev, dev, noack_map);  }  struct get_key_cookie { @@ -2210,8 +2349,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)  	    !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))  		return -ENOENT; -	err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise, -				 mac_addr, &cookie, get_key_callback); +	err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie, +			   get_key_callback);  	if (err)  		goto free_msg; @@ -2259,7 +2398,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)  		if (err)  			goto out; -		err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, +		err = rdev_set_default_key(rdev, dev, key.idx,  						 key.def_uni, key.def_multi);  		if (err) @@ -2283,8 +2422,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)  		if (err)  			goto out; -		err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, -						      dev, key.idx); +		err = rdev_set_default_mgmt_key(rdev, dev, key.idx);  		if (err)  			goto out; @@ -2340,9 +2478,9 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)  	wdev_lock(dev->ieee80211_ptr);  	err = nl80211_key_allowed(dev->ieee80211_ptr);  	if (!err) -		err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, -					 key.type == NL80211_KEYTYPE_PAIRWISE, -					 mac_addr, &key.p); +		err = rdev_add_key(rdev, dev, key.idx, +				   key.type == NL80211_KEYTYPE_PAIRWISE, +				    mac_addr, &key.p);  	wdev_unlock(dev->ieee80211_ptr);  	return err; @@ -2386,9 +2524,9 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)  		err = -ENOENT;  	if (!err) -		err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, -					 key.type == NL80211_KEYTYPE_PAIRWISE, -					 mac_addr); +		err = rdev_del_key(rdev, dev, key.idx, +				   key.type == NL80211_KEYTYPE_PAIRWISE, +				   mac_addr);  #ifdef CONFIG_CFG80211_WEXT  	if (!err) { @@ -2403,6 +2541,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)  	return err;  } +/* This function returns an error or the number of nested attributes */ +static int validate_acl_mac_addrs(struct nlattr *nl_attr) +{ +	struct nlattr *attr; +	int n_entries = 0, tmp; + +	nla_for_each_nested(attr, nl_attr, tmp) { +		if (nla_len(attr) != ETH_ALEN) +			return -EINVAL; + +		n_entries++; +	} + +	return n_entries; +} + +/* + * This function parses ACL information and allocates memory for ACL data. + * On successful return, the calling function is responsible to free the + * ACL buffer returned by this function. + */ +static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy, +						struct genl_info *info) +{ +	enum nl80211_acl_policy acl_policy; +	struct nlattr *attr; +	struct cfg80211_acl_data *acl; +	int i = 0, n_entries, tmp; + +	if (!wiphy->max_acl_mac_addrs) +		return ERR_PTR(-EOPNOTSUPP); + +	if (!info->attrs[NL80211_ATTR_ACL_POLICY]) +		return ERR_PTR(-EINVAL); + +	acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]); +	if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED && +	    acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED) +		return ERR_PTR(-EINVAL); + +	if (!info->attrs[NL80211_ATTR_MAC_ADDRS]) +		return ERR_PTR(-EINVAL); + +	n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]); +	if (n_entries < 0) +		return ERR_PTR(n_entries); + +	if (n_entries > wiphy->max_acl_mac_addrs) +		return ERR_PTR(-ENOTSUPP); + +	acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries), +		      GFP_KERNEL); +	if (!acl) +		return ERR_PTR(-ENOMEM); + +	nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) { +		memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN); +		i++; +	} + +	acl->n_acl_entries = n_entries; +	acl->acl_policy = acl_policy; + +	return acl; +} + +static int nl80211_set_mac_acl(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 cfg80211_acl_data *acl; +	int err; + +	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && +	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) +		return -EOPNOTSUPP; + +	if (!dev->ieee80211_ptr->beacon_interval) +		return -EINVAL; + +	acl = parse_acl_data(&rdev->wiphy, info); +	if (IS_ERR(acl)) +		return PTR_ERR(acl); + +	err = rdev_set_mac_acl(rdev, dev, acl); + +	kfree(acl); + +	return err; +} +  static int nl80211_parse_beacon(struct genl_info *info,  				struct cfg80211_beacon_data *bcn)  { @@ -2476,11 +2705,10 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,  		    wdev->iftype != NL80211_IFTYPE_P2P_GO)  			continue; -		if (!wdev->preset_chan) +		if (!wdev->preset_chandef.chan)  			continue; -		params->channel = wdev->preset_chan; -		params->channel_type = wdev->preset_chantype; +		params->chandef = wdev->preset_chandef;  		ret = true;  		break;  	} @@ -2490,6 +2718,30 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,  	return ret;  } +static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, +				    enum nl80211_auth_type auth_type, +				    enum nl80211_commands cmd) +{ +	if (auth_type > NL80211_AUTHTYPE_MAX) +		return false; + +	switch (cmd) { +	case NL80211_CMD_AUTHENTICATE: +		if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && +		    auth_type == NL80211_AUTHTYPE_SAE) +			return false; +		return true; +	case NL80211_CMD_CONNECT: +	case NL80211_CMD_START_AP: +		/* SAE not supported yet */ +		if (auth_type == NL80211_AUTHTYPE_SAE) +			return false; +		return true; +	default: +		return false; +	} +} +  static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -2497,6 +2749,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_ap_settings params;  	int err; +	u8 radar_detect_width = 0;  	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&  	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) @@ -2559,7 +2812,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {  		params.auth_type = nla_get_u32(  			info->attrs[NL80211_ATTR_AUTH_TYPE]); -		if (!nl80211_valid_auth_type(params.auth_type)) +		if (!nl80211_valid_auth_type(rdev, params.auth_type, +					     NL80211_CMD_START_AP))  			return -EINVAL;  	} else  		params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; @@ -2576,44 +2830,79 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)  			info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);  	} -	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { -		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - -		if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && -		    !nl80211_valid_channel_type(info, &channel_type)) +	if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { +		if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) +			return -EINVAL; +		params.p2p_ctwindow = +			nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]); +		if (params.p2p_ctwindow > 127) +			return -EINVAL; +		if (params.p2p_ctwindow != 0 && +		    !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))  			return -EINVAL; +	} + +	if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { +		u8 tmp; -		params.channel = rdev_freq_to_chan(rdev, -			nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), -			channel_type); -		if (!params.channel) +		if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) +			return -EINVAL; +		tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); +		if (tmp > 1)  			return -EINVAL; -		params.channel_type = channel_type; -	} else if (wdev->preset_chan) { -		params.channel = wdev->preset_chan; -		params.channel_type = wdev->preset_chantype; +		params.p2p_opp_ps = tmp; +		if (params.p2p_opp_ps != 0 && +		    !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) +			return -EINVAL; +	} + +	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { +		err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); +		if (err) +			return err; +	} else if (wdev->preset_chandef.chan) { +		params.chandef = wdev->preset_chandef;  	} else if (!nl80211_get_ap_channel(rdev, ¶ms))  		return -EINVAL; -	if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel, -					  params.channel_type)) +	if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))  		return -EINVAL; +	err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); +	if (err < 0) +		return err; +	if (err) { +		radar_detect_width = BIT(params.chandef.width); +		params.radar_required = true; +	} +  	mutex_lock(&rdev->devlist_mtx); -	err = cfg80211_can_use_chan(rdev, wdev, params.channel, -				    CHAN_MODE_SHARED); +	err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, +					   params.chandef.chan, +					   CHAN_MODE_SHARED, +					   radar_detect_width);  	mutex_unlock(&rdev->devlist_mtx);  	if (err)  		return err; -	err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); +	if (info->attrs[NL80211_ATTR_ACL_POLICY]) { +		params.acl = parse_acl_data(&rdev->wiphy, info); +		if (IS_ERR(params.acl)) +			return PTR_ERR(params.acl); +	} + +	err = rdev_start_ap(rdev, dev, ¶ms);  	if (!err) { -		wdev->preset_chan = params.channel; -		wdev->preset_chantype = params.channel_type; +		wdev->preset_chandef = params.chandef;  		wdev->beacon_interval = params.beacon_interval; -		wdev->channel = params.channel; +		wdev->channel = params.chandef.chan; +		wdev->ssid_len = params.ssid_len; +		memcpy(wdev->ssid, params.ssid, wdev->ssid_len);  	} + +	kfree(params.acl); +  	return err;  } @@ -2639,7 +2928,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)  	if (err)  		return err; -	return rdev->ops->change_beacon(&rdev->wiphy, dev, ¶ms); +	return rdev_change_beacon(rdev, dev, ¶ms);  }  static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) @@ -2744,29 +3033,52 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,  	rate = nla_nest_start(msg, attr);  	if (!rate) -		goto nla_put_failure; +		return false;  	/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */  	bitrate = cfg80211_calculate_bitrate(info);  	/* report 16-bit bitrate only if we can */  	bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; -	if ((bitrate > 0 && -	     nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) || -	    (bitrate_compat > 0 && -	     nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) || -	    ((info->flags & RATE_INFO_FLAGS_MCS) && -	     nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) || -	    ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) && -	     nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) || -	    ((info->flags & RATE_INFO_FLAGS_SHORT_GI) && -	     nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))) -		goto nla_put_failure; +	if (bitrate > 0 && +	    nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) +		return false; +	if (bitrate_compat > 0 && +	    nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) +		return false; + +	if (info->flags & RATE_INFO_FLAGS_MCS) { +		if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) +			return false; +		if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && +		    nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) +			return false; +		if (info->flags & RATE_INFO_FLAGS_SHORT_GI && +		    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)) +			return false; +	} else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) { +		if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs)) +			return false; +		if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss)) +			return false; +		if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && +		    nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) +			return false; +		if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH && +		    nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH)) +			return false; +		if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH && +		    nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH)) +			return false; +		if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH && +		    nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH)) +			return false; +		if (info->flags & RATE_INFO_FLAGS_SHORT_GI && +		    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)) +			return false; +	}  	nla_nest_end(msg, rate);  	return true; - -nla_put_failure: -	return false;  }  static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, @@ -2798,12 +3110,22 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,  	    nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,  			sinfo->inactive_time))  		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_RX_BYTES) && +	if ((sinfo->filled & (STATION_INFO_RX_BYTES | +			      STATION_INFO_RX_BYTES64)) &&  	    nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES, -			sinfo->rx_bytes)) +			(u32)sinfo->rx_bytes))  		goto nla_put_failure; -	if ((sinfo->filled & STATION_INFO_TX_BYTES) && +	if ((sinfo->filled & (STATION_INFO_TX_BYTES | +			      NL80211_STA_INFO_TX_BYTES64)) &&  	    nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES, +			(u32)sinfo->tx_bytes)) +		goto nla_put_failure; +	if ((sinfo->filled & STATION_INFO_RX_BYTES64) && +	    nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64, +			sinfo->rx_bytes)) +		goto nla_put_failure; +	if ((sinfo->filled & STATION_INFO_TX_BYTES64) && +	    nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64,  			sinfo->tx_bytes))  		goto nla_put_failure;  	if ((sinfo->filled & STATION_INFO_LLID) && @@ -2860,6 +3182,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,  	    nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,  			sinfo->beacon_loss_count))  		goto nla_put_failure; +	if ((sinfo->filled & STATION_INFO_LOCAL_PM) && +	    nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM, +			sinfo->local_pm)) +		goto nla_put_failure; +	if ((sinfo->filled & STATION_INFO_PEER_PM) && +	    nla_put_u32(msg, NL80211_STA_INFO_PEER_PM, +			sinfo->peer_pm)) +		goto nla_put_failure; +	if ((sinfo->filled & STATION_INFO_NONPEER_PM) && +	    nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM, +			sinfo->nonpeer_pm)) +		goto nla_put_failure;  	if (sinfo->filled & STATION_INFO_BSS_PARAM) {  		bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);  		if (!bss_param) @@ -2923,8 +3257,8 @@ static int nl80211_dump_station(struct sk_buff *skb,  	while (1) {  		memset(&sinfo, 0, sizeof(sinfo)); -		err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, -					     mac_addr, &sinfo); +		err = rdev_dump_station(dev, netdev, sta_idx, +					mac_addr, &sinfo);  		if (err == -ENOENT)  			break;  		if (err) @@ -2969,7 +3303,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)  	if (!rdev->ops->get_station)  		return -EOPNOTSUPP; -	err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); +	err = rdev_get_station(rdev, dev, mac_addr, &sinfo);  	if (err)  		return err; @@ -3019,6 +3353,54 @@ static struct net_device *get_vlan(struct genl_info *info,  	return ERR_PTR(ret);  } +static struct nla_policy +nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { +	[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, +	[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, +}; + +static int nl80211_set_station_tdls(struct genl_info *info, +				    struct station_parameters *params) +{ +	struct nlattr *tb[NL80211_STA_WME_MAX + 1]; +	struct nlattr *nla; +	int err; + +	/* Dummy STA entry gets updated once the peer capabilities are known */ +	if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) +		params->ht_capa = +			nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); +	if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) +		params->vht_capa = +			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + +	/* parse WME attributes if present */ +	if (!info->attrs[NL80211_ATTR_STA_WME]) +		return 0; + +	nla = info->attrs[NL80211_ATTR_STA_WME]; +	err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, +			       nl80211_sta_wme_policy); +	if (err) +		return err; + +	if (tb[NL80211_STA_WME_UAPSD_QUEUES]) +		params->uapsd_queues = nla_get_u8( +			tb[NL80211_STA_WME_UAPSD_QUEUES]); +	if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) +		return -EINVAL; + +	if (tb[NL80211_STA_WME_MAX_SP]) +		params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); + +	if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) +		return -EINVAL; + +	params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; + +	return 0; +} +  static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -3047,13 +3429,21 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  			nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);  	} -	if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) -		params.listen_interval = -		    nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); +	if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { +		params.capability = +			nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]); +		params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; +	} -	if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) -		params.ht_capa = -			nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); +	if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { +		params.ext_capab = +			nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); +		params.ext_capab_len = +			nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); +	} + +	if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) +		return -EINVAL;  	if (!rdev->ops->change_station)  		return -EOPNOTSUPP; @@ -3069,6 +3459,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  		params.plink_state =  		    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); +	if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) { +		enum nl80211_mesh_power_mode pm = nla_get_u32( +			info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]); + +		if (pm <= NL80211_MESH_POWER_UNKNOWN || +		    pm > NL80211_MESH_POWER_MAX) +			return -EINVAL; + +		params.local_pm = pm; +	} +  	switch (dev->ieee80211_ptr->iftype) {  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_AP_VLAN: @@ -3076,6 +3477,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  		/* disallow mesh-specific things */  		if (params.plink_action)  			return -EINVAL; +		if (params.local_pm) +			return -EINVAL;  		/* TDLS can't be set, ... */  		if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) @@ -3090,11 +3493,32 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  		/* accept only the listed bits */  		if (params.sta_flags_mask &  				~(BIT(NL80211_STA_FLAG_AUTHORIZED) | +				  BIT(NL80211_STA_FLAG_AUTHENTICATED) | +				  BIT(NL80211_STA_FLAG_ASSOCIATED) |  				  BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |  				  BIT(NL80211_STA_FLAG_WME) |  				  BIT(NL80211_STA_FLAG_MFP)))  			return -EINVAL; +		/* but authenticated/associated only if driver handles it */ +		if (!(rdev->wiphy.features & +				NL80211_FEATURE_FULL_AP_CLIENT_STATE) && +		    params.sta_flags_mask & +				(BIT(NL80211_STA_FLAG_AUTHENTICATED) | +				 BIT(NL80211_STA_FLAG_ASSOCIATED))) +			return -EINVAL; + +		/* reject other things that can't change */ +		if (params.supported_rates) +			return -EINVAL; +		if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) +			return -EINVAL; +		if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) +			return -EINVAL; +		if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || +		    info->attrs[NL80211_ATTR_VHT_CAPABILITY]) +			return -EINVAL; +  		/* must be last in here for error handling */  		params.vlan = get_vlan(info, rdev);  		if (IS_ERR(params.vlan)) @@ -3109,14 +3533,28 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  		 * to change the flag.  		 */  		params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); -		/* fall through */ +		/* Include parameters for TDLS peer (driver will check) */ +		err = nl80211_set_station_tdls(info, ¶ms); +		if (err) +			return err; +		/* disallow things sta doesn't support */ +		if (params.plink_action) +			return -EINVAL; +		if (params.local_pm) +			return -EINVAL; +		/* reject any changes other than AUTHORIZED or WME (for TDLS) */ +		if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | +					      BIT(NL80211_STA_FLAG_WME))) +			return -EINVAL; +		break;  	case NL80211_IFTYPE_ADHOC:  		/* disallow things sta doesn't support */  		if (params.plink_action)  			return -EINVAL; -		if (params.ht_capa) +		if (params.local_pm)  			return -EINVAL; -		if (params.listen_interval >= 0) +		if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || +		    info->attrs[NL80211_ATTR_VHT_CAPABILITY])  			return -EINVAL;  		/* reject any changes other than AUTHORIZED */  		if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) @@ -3126,9 +3564,14 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  		/* disallow things mesh doesn't support */  		if (params.vlan)  			return -EINVAL; -		if (params.ht_capa) +		if (params.supported_rates)  			return -EINVAL; -		if (params.listen_interval >= 0) +		if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) +			return -EINVAL; +		if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) +			return -EINVAL; +		if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || +		    info->attrs[NL80211_ATTR_VHT_CAPABILITY])  			return -EINVAL;  		/*  		 * No special handling for TDLS here -- the userspace @@ -3146,7 +3589,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  	/* be aware of params.vlan when changing code here */ -	err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); +	err = rdev_change_station(rdev, dev, mac_addr, ¶ms);  	if (params.vlan)  		dev_put(params.vlan); @@ -3154,12 +3597,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)  	return err;  } -static struct nla_policy -nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { -	[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, -	[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, -}; -  static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -3194,10 +3631,27 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)  	if (!params.aid || params.aid > IEEE80211_MAX_AID)  		return -EINVAL; +	if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { +		params.capability = +			nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]); +		params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; +	} + +	if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { +		params.ext_capab = +			nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); +		params.ext_capab_len = +			nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); +	} +  	if (info->attrs[NL80211_ATTR_HT_CAPABILITY])  		params.ht_capa =  			nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); +	if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) +		params.vht_capa = +			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); +  	if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])  		params.plink_action =  		    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); @@ -3248,17 +3702,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)  		/* but don't bother the driver with it */  		params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); +		/* allow authenticated/associated only if driver handles it */ +		if (!(rdev->wiphy.features & +				NL80211_FEATURE_FULL_AP_CLIENT_STATE) && +		    params.sta_flags_mask & +				(BIT(NL80211_STA_FLAG_AUTHENTICATED) | +				 BIT(NL80211_STA_FLAG_ASSOCIATED))) +			return -EINVAL; +  		/* must be last in here for error handling */  		params.vlan = get_vlan(info, rdev);  		if (IS_ERR(params.vlan))  			return PTR_ERR(params.vlan);  		break;  	case NL80211_IFTYPE_MESH_POINT: +		/* associated is disallowed */ +		if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) +			return -EINVAL;  		/* TDLS peers cannot be added */  		if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))  			return -EINVAL;  		break;  	case NL80211_IFTYPE_STATION: +		/* associated is disallowed */ +		if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) +			return -EINVAL;  		/* Only TDLS peers can be added */  		if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))  			return -EINVAL; @@ -3275,7 +3743,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)  	/* be aware of params.vlan when changing code here */ -	err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); +	err = rdev_add_station(rdev, dev, mac_addr, ¶ms);  	if (params.vlan)  		dev_put(params.vlan); @@ -3300,7 +3768,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)  	if (!rdev->ops->del_station)  		return -EOPNOTSUPP; -	return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); +	return rdev_del_station(rdev, dev, mac_addr);  }  static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, @@ -3382,8 +3850,8 @@ static int nl80211_dump_mpath(struct sk_buff *skb,  	}  	while (1) { -		err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, -					   dst, next_hop, &pinfo); +		err = rdev_dump_mpath(dev, netdev, path_idx, dst, next_hop, +				      &pinfo);  		if (err == -ENOENT)  			break;  		if (err) @@ -3430,7 +3898,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)  	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)  		return -EOPNOTSUPP; -	err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); +	err = rdev_get_mpath(rdev, dev, dst, next_hop, &pinfo);  	if (err)  		return err; @@ -3469,7 +3937,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)  	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)  		return -EOPNOTSUPP; -	return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); +	return rdev_change_mpath(rdev, dev, dst, next_hop);  }  static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) @@ -3494,7 +3962,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)  	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)  		return -EOPNOTSUPP; -	return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); +	return rdev_add_mpath(rdev, dev, dst, next_hop);  }  static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) @@ -3509,7 +3977,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)  	if (!rdev->ops->del_mpath)  		return -EOPNOTSUPP; -	return rdev->ops->del_mpath(&rdev->wiphy, dev, dst); +	return rdev_del_mpath(rdev, dev, dst);  }  static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) @@ -3525,6 +3993,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)  	params.use_short_slot_time = -1;  	params.ap_isolate = -1;  	params.ht_opmode = -1; +	params.p2p_ctwindow = -1; +	params.p2p_opp_ps = -1;  	if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])  		params.use_cts_prot = @@ -3547,6 +4017,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)  		params.ht_opmode =  			nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); +	if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { +		if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) +			return -EINVAL; +		params.p2p_ctwindow = +			nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]); +		if (params.p2p_ctwindow < 0) +			return -EINVAL; +		if (params.p2p_ctwindow != 0 && +		    !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) +			return -EINVAL; +	} + +	if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { +		u8 tmp; + +		if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) +			return -EINVAL; +		tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]); +		if (tmp > 1) +			return -EINVAL; +		params.p2p_opp_ps = tmp; +		if (params.p2p_opp_ps && +		    !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) +			return -EINVAL; +	} +  	if (!rdev->ops->change_bss)  		return -EOPNOTSUPP; @@ -3554,7 +4050,7 @@ 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->ops->change_bss(&rdev->wiphy, dev, ¶ms); +	return rdev_change_bss(rdev, dev, ¶ms);  }  static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { @@ -3614,12 +4110,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)  	 * window between nl80211_init() and regulatory_init(), if that is  	 * even possible.  	 */ -	mutex_lock(&cfg80211_mutex); -	if (unlikely(!cfg80211_regdomain)) { -		mutex_unlock(&cfg80211_mutex); +	if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))  		return -EINPROGRESS; -	} -	mutex_unlock(&cfg80211_mutex);  	if (!info->attrs[NL80211_ATTR_REG_ALPHA2])  		return -EINVAL; @@ -3668,8 +4160,7 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,  	if (!wdev->mesh_id_len)  		memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));  	else -		err = rdev->ops->get_mesh_config(&rdev->wiphy, dev, -						 &cur_params); +		err = rdev_get_mesh_config(rdev, dev, &cur_params);  	wdev_unlock(wdev);  	if (err) @@ -3736,7 +4227,11 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,  	    nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,  			cur_params.dot11MeshHWMProotInterval) ||  	    nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, -			cur_params.dot11MeshHWMPconfirmationInterval)) +			cur_params.dot11MeshHWMPconfirmationInterval) || +	    nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE, +			cur_params.power_mode) || +	    nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW, +			cur_params.dot11MeshAwakeWindowDuration))  		goto nla_put_failure;  	nla_nest_end(msg, pinfoattr);  	genlmsg_end(msg, hdr); @@ -3775,6 +4270,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A  	[NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },  	[NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },  	[NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, +	[NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 }, +	[NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },  };  static const struct nla_policy @@ -3795,13 +4292,15 @@ static int nl80211_parse_mesh_config(struct genl_info *info,  	struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];  	u32 mask = 0; -#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ -do {\ -	if (table[attr_num]) {\ -		cfg->param = nla_fn(table[attr_num]); \ -		mask |= (1 << (attr_num - 1)); \ -	} \ -} while (0);\ +#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \ +do {									    \ +	if (tb[attr]) {							    \ +		if (fn(tb[attr]) < min || fn(tb[attr]) > max)		    \ +			return -EINVAL;					    \ +		cfg->param = fn(tb[attr]);				    \ +		mask |= (1 << (attr - 1));				    \ +	}								    \ +} while (0)  	if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) @@ -3816,83 +4315,98 @@ do {\  	BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);  	/* Fill in the params struct */ -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255,  				  mask, NL80211_MESHCONF_RETRY_TIMEOUT,  				  nla_get_u16); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255,  				  mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,  				  nla_get_u16); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255,  				  mask, NL80211_MESHCONF_HOLDING_TIMEOUT,  				  nla_get_u16); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255,  				  mask, NL80211_MESHCONF_MAX_PEER_LINKS,  				  nla_get_u16); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16,  				  mask, NL80211_MESHCONF_MAX_RETRIES,  				  nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255,  				  mask, NL80211_MESHCONF_TTL, nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255,  				  mask, NL80211_MESHCONF_ELEMENT_TTL,  				  nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1,  				  mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,  				  nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, +				  1, 255, mask,  				  NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,  				  nla_get_u32); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255,  				  mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,  				  nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535,  				  mask, NL80211_MESHCONF_PATH_REFRESH_TIME,  				  nla_get_u32); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535,  				  mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,  				  nla_get_u16); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, +				  1, 65535, mask,  				  NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,  				  nla_get_u32);  	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, -				  mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, +				  1, 65535, mask, +				  NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,  				  nla_get_u16);  	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, -				  mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, +				  1, 65535, mask, +				  NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,  				  nla_get_u16);  	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, -				  dot11MeshHWMPnetDiameterTraversalTime, mask, +				  dot11MeshHWMPnetDiameterTraversalTime, +				  1, 65535, mask,  				  NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,  				  nla_get_u16); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, -				  NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, -				  NL80211_MESHCONF_HWMP_RANN_INTERVAL, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4, +				  mask, NL80211_MESHCONF_HWMP_ROOTMODE, +				  nla_get_u8); +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535, +				  mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL,  				  nla_get_u16);  	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, -				  dot11MeshGateAnnouncementProtocol, mask, -				  NL80211_MESHCONF_GATE_ANNOUNCEMENTS, +				  dot11MeshGateAnnouncementProtocol, 0, 1, +				  mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,  				  nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,  				  mask, NL80211_MESHCONF_FORWARDING,  				  nla_get_u8); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255,  				  mask, NL80211_MESHCONF_RSSI_THRESHOLD,  				  nla_get_u32); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16,  				  mask, NL80211_MESHCONF_HT_OPMODE,  				  nla_get_u16);  	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, -				  mask, +				  1, 65535, mask,  				  NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,  				  nla_get_u32); -	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535,  				  mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,  				  nla_get_u16);  	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, -				  dot11MeshHWMPconfirmationInterval, mask, +				  dot11MeshHWMPconfirmationInterval, +				  1, 65535, mask,  				  NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,  				  nla_get_u16); +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode, +				  NL80211_MESH_POWER_ACTIVE, +				  NL80211_MESH_POWER_MAX, +				  mask, NL80211_MESHCONF_POWER_MODE, +				  nla_get_u32); +	FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, +				  0, 65535, mask, +				  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);  	if (mask_out)  		*mask_out = mask; @@ -3971,8 +4485,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,  		err = -ENOLINK;  	if (!err) -		err = rdev->ops->update_mesh_config(&rdev->wiphy, dev, -						    mask, &cfg); +		err = rdev_update_mesh_config(rdev, dev, mask, &cfg);  	wdev_unlock(wdev); @@ -3981,6 +4494,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,  static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  { +	const struct ieee80211_regdomain *regdom;  	struct sk_buff *msg;  	void *hdr = NULL;  	struct nlattr *nl_reg_rules; @@ -4003,35 +4517,36 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  	if (!hdr)  		goto put_failure; -	if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, -			   cfg80211_regdomain->alpha2) || -	    (cfg80211_regdomain->dfs_region && -	     nla_put_u8(msg, NL80211_ATTR_DFS_REGION, -			cfg80211_regdomain->dfs_region))) -		goto nla_put_failure; -  	if (reg_last_request_cell_base() &&  	    nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,  			NL80211_USER_REG_HINT_CELL_BASE))  		goto nla_put_failure; +	rcu_read_lock(); +	regdom = rcu_dereference(cfg80211_regdomain); + +	if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || +	    (regdom->dfs_region && +	     nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) +		goto nla_put_failure_rcu; +  	nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);  	if (!nl_reg_rules) -		goto nla_put_failure; +		goto nla_put_failure_rcu; -	for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { +	for (i = 0; i < regdom->n_reg_rules; i++) {  		struct nlattr *nl_reg_rule;  		const struct ieee80211_reg_rule *reg_rule;  		const struct ieee80211_freq_range *freq_range;  		const struct ieee80211_power_rule *power_rule; -		reg_rule = &cfg80211_regdomain->reg_rules[i]; +		reg_rule = ®dom->reg_rules[i];  		freq_range = ®_rule->freq_range;  		power_rule = ®_rule->power_rule;  		nl_reg_rule = nla_nest_start(msg, i);  		if (!nl_reg_rule) -			goto nla_put_failure; +			goto nla_put_failure_rcu;  		if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,  				reg_rule->flags) || @@ -4045,10 +4560,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  				power_rule->max_antenna_gain) ||  		    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,  				power_rule->max_eirp)) -			goto nla_put_failure; +			goto nla_put_failure_rcu;  		nla_nest_end(msg, nl_reg_rule);  	} +	rcu_read_unlock();  	nla_nest_end(msg, nl_reg_rules); @@ -4056,6 +4572,8 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  	err = genlmsg_reply(msg, info);  	goto out; +nla_put_failure_rcu: +	rcu_read_unlock();  nla_put_failure:  	genlmsg_cancel(msg, hdr);  put_failure: @@ -4088,27 +4606,18 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)  		dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);  	nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], -			rem_reg_rules) { +			    rem_reg_rules) {  		num_rules++;  		if (num_rules > NL80211_MAX_SUPP_REG_RULES)  			return -EINVAL;  	} -	mutex_lock(&cfg80211_mutex); - -	if (!reg_is_valid_request(alpha2)) { -		r = -EINVAL; -		goto bad_reg; -	} -  	size_of_regd = sizeof(struct ieee80211_regdomain) + -		(num_rules * sizeof(struct ieee80211_reg_rule)); +		       num_rules * sizeof(struct ieee80211_reg_rule);  	rd = kzalloc(size_of_regd, GFP_KERNEL); -	if (!rd) { -		r = -ENOMEM; -		goto bad_reg; -	} +	if (!rd) +		return -ENOMEM;  	rd->n_reg_rules = num_rules;  	rd->alpha2[0] = alpha2[0]; @@ -4122,10 +4631,10 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)  		rd->dfs_region = dfs_region;  	nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], -			rem_reg_rules) { +			    rem_reg_rules) {  		nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, -			nla_data(nl_reg_rule), nla_len(nl_reg_rule), -			reg_rule_policy); +			  nla_data(nl_reg_rule), nla_len(nl_reg_rule), +			  reg_rule_policy);  		r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);  		if (r)  			goto bad_reg; @@ -4138,16 +4647,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)  		}  	} -	BUG_ON(rule_idx != num_rules); +	mutex_lock(&cfg80211_mutex);  	r = set_regdom(rd); - +	/* set_regdom took ownership */ +	rd = NULL;  	mutex_unlock(&cfg80211_mutex); -	return r; -   bad_reg: -	mutex_unlock(&cfg80211_mutex);  	kfree(rd);  	return r;  } @@ -4337,14 +4844,27 @@ 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))) { +			err = -EOPNOTSUPP; +			goto out_free; +		} +	} +  	request->no_cck =  		nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);  	request->wdev = wdev;  	request->wiphy = &rdev->wiphy; +	request->scan_start = jiffies;  	rdev->scan_req = request; -	err = rdev->ops->scan(&rdev->wiphy, request); +	err = rdev_scan(rdev, request);  	if (!err) {  		nl80211_send_scan_start(rdev, wdev); @@ -4568,11 +5088,24 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  		       request->ie_len);  	} +	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))) { +			err = -EOPNOTSUPP; +			goto out_free; +		} +	} +  	request->dev = dev;  	request->wiphy = &rdev->wiphy;  	request->interval = interval; +	request->scan_start = jiffies; -	err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); +	err = rdev_sched_scan_start(rdev, dev, request);  	if (!err) {  		rdev->sched_scan_req = request;  		nl80211_send_sched_scan(rdev, dev, @@ -4604,6 +5137,54 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,  	return err;  } +static int nl80211_start_radar_detection(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 cfg80211_chan_def chandef; +	int err; + +	err = nl80211_parse_chandef(rdev, info, &chandef); +	if (err) +		return err; + +	if (wdev->cac_started) +		return -EBUSY; + +	err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef); +	if (err < 0) +		return err; + +	if (err == 0) +		return -EINVAL; + +	if (chandef.chan->dfs_state != NL80211_DFS_USABLE) +		return -EINVAL; + +	if (!rdev->ops->start_radar_detection) +		return -EOPNOTSUPP; + +	mutex_lock(&rdev->devlist_mtx); +	err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, +					   chandef.chan, CHAN_MODE_SHARED, +					   BIT(chandef.width)); +	if (err) +		goto err_locked; + +	err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef); +	if (!err) { +		wdev->channel = chandef.chan; +		wdev->cac_started = true; +		wdev->cac_start_time = jiffies; +	} +err_locked: +	mutex_unlock(&rdev->devlist_mtx); + +	return err; +} +  static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,  			    u32 seq, int flags,  			    struct cfg80211_registered_device *rdev, @@ -4611,8 +5192,10 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,  			    struct cfg80211_internal_bss *intbss)  {  	struct cfg80211_bss *res = &intbss->pub; +	const struct cfg80211_bss_ies *ies;  	void *hdr;  	struct nlattr *bss; +	bool tsf = false;  	ASSERT_WDEV_LOCK(wdev); @@ -4631,19 +5214,29 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,  	if (!bss)  		goto nla_put_failure;  	if ((!is_zero_ether_addr(res->bssid) && -	     nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) || -	    (res->information_elements && res->len_information_elements && -	     nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, -		     res->len_information_elements, -		     res->information_elements)) || -	    (res->beacon_ies && res->len_beacon_ies && -	     res->beacon_ies != res->information_elements && -	     nla_put(msg, NL80211_BSS_BEACON_IES, -		     res->len_beacon_ies, res->beacon_ies))) -		goto nla_put_failure; -	if (res->tsf && -	    nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) +	     nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))  		goto nla_put_failure; + +	rcu_read_lock(); +	ies = rcu_dereference(res->ies); +	if (ies) { +		if (nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) +			goto fail_unlock_rcu; +		tsf = true; +		if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, +					ies->len, ies->data)) +			goto fail_unlock_rcu; +	} +	ies = rcu_dereference(res->beacon_ies); +	if (ies) { +		if (!tsf && nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) +			goto fail_unlock_rcu; +		if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, +					ies->len, ies->data)) +			goto fail_unlock_rcu; +	} +	rcu_read_unlock(); +  	if (res->beacon_interval &&  	    nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval))  		goto nla_put_failure; @@ -4688,6 +5281,8 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,  	return genlmsg_end(msg, hdr); + fail_unlock_rcu: +	rcu_read_unlock();   nla_put_failure:  	genlmsg_cancel(msg, hdr);  	return -EMSGSIZE; @@ -4815,8 +5410,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,  	while (1) {  		struct ieee80211_channel *chan; -		res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, -					    &survey); +		res = rdev_dump_survey(dev, netdev, survey_idx, &survey);  		if (res == -ENOENT)  			break;  		if (res) @@ -4852,11 +5446,6 @@ static int nl80211_dump_survey(struct sk_buff *skb,  	return res;  } -static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) -{ -	return auth_type <= NL80211_AUTHTYPE_MAX; -} -  static bool nl80211_valid_wpa_versions(u32 wpa_versions)  {  	return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | @@ -4868,8 +5457,8 @@ static int nl80211_authenticate(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 ieee80211_channel *chan; -	const u8 *bssid, *ssid, *ie = NULL; -	int err, ssid_len, ie_len = 0; +	const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL; +	int err, ssid_len, ie_len = 0, sae_data_len = 0;  	enum nl80211_auth_type auth_type;  	struct key_parse key;  	bool local_state_change; @@ -4945,9 +5534,23 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)  	}  	auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); -	if (!nl80211_valid_auth_type(auth_type)) +	if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE))  		return -EINVAL; +	if (auth_type == NL80211_AUTHTYPE_SAE && +	    !info->attrs[NL80211_ATTR_SAE_DATA]) +		return -EINVAL; + +	if (info->attrs[NL80211_ATTR_SAE_DATA]) { +		if (auth_type != NL80211_AUTHTYPE_SAE) +			return -EINVAL; +		sae_data = nla_data(info->attrs[NL80211_ATTR_SAE_DATA]); +		sae_data_len = nla_len(info->attrs[NL80211_ATTR_SAE_DATA]); +		/* need to include at least Auth Transaction and Status Code */ +		if (sae_data_len < 4) +			return -EINVAL; +	} +  	local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];  	/* @@ -4959,7 +5562,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)  	return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,  				  ssid, ssid_len, ie, ie_len, -				  key.p.key, key.p.key_len, key.idx); +				  key.p.key, key.p.key_len, key.idx, +				  sae_data, sae_data_len);  }  static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, @@ -5250,8 +5854,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)  	if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))  		return -EINVAL; -	if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || -	    !info->attrs[NL80211_ATTR_SSID] || +	if (!info->attrs[NL80211_ATTR_SSID] ||  	    !nla_len(info->attrs[NL80211_ATTR_SSID]))  		return -EINVAL; @@ -5286,34 +5889,17 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)  		ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);  	} -	if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { -		enum nl80211_channel_type channel_type; - -		if (!nl80211_valid_channel_type(info, &channel_type)) -			return -EINVAL; - -		if (channel_type != NL80211_CHAN_NO_HT && -		    !(wiphy->features & NL80211_FEATURE_HT_IBSS)) -			return -EINVAL; - -		ibss.channel_type = channel_type; -	} else { -		ibss.channel_type = NL80211_CHAN_NO_HT; -	} +	err = nl80211_parse_chandef(rdev, info, &ibss.chandef); +	if (err) +		return err; -	ibss.channel = rdev_freq_to_chan(rdev, -		nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), -		ibss.channel_type); -	if (!ibss.channel || -	    ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || -	    ibss.channel->flags & IEEE80211_CHAN_DISABLED) +	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))  		return -EINVAL; -	/* Both channels should be able to initiate communication */ -	if ((ibss.channel_type == NL80211_CHAN_HT40PLUS || -	     ibss.channel_type == NL80211_CHAN_HT40MINUS) && -	    !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel, -					  ibss.channel_type)) +	if (ibss.chandef.width > NL80211_CHAN_WIDTH_40) +		return -EINVAL; +	if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && +	    !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))  		return -EINVAL;  	ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; @@ -5325,7 +5911,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)  		int n_rates =  			nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);  		struct ieee80211_supported_band *sband = -			wiphy->bands[ibss.channel->band]; +			wiphy->bands[ibss.chandef.chan->band];  		err = ieee80211_get_ratemask(sband, rates, n_rates,  					     &ibss.basic_rates); @@ -5339,10 +5925,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)  		return -EINVAL;  	if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { +		bool no_ht = false; +  		connkeys = nl80211_parse_connkeys(rdev, -					info->attrs[NL80211_ATTR_KEYS]); +					  info->attrs[NL80211_ATTR_KEYS], +					  &no_ht);  		if (IS_ERR(connkeys))  			return PTR_ERR(connkeys); + +		if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) && +		    no_ht) { +			kfree(connkeys); +			return -EINVAL; +		}  	}  	ibss.control_port = @@ -5368,6 +5963,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)  	return cfg80211_leave_ibss(rdev, dev, false);  } +static int nl80211_set_mcast_rate(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]; +	int mcast_rate[IEEE80211_NUM_BANDS]; +	u32 nla_rate; +	int err; + +	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && +	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) +		return -EOPNOTSUPP; + +	if (!rdev->ops->set_mcast_rate) +		return -EOPNOTSUPP; + +	memset(mcast_rate, 0, sizeof(mcast_rate)); + +	if (!info->attrs[NL80211_ATTR_MCAST_RATE]) +		return -EINVAL; + +	nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]); +	if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate)) +		return -EINVAL; + +	err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate); + +	return err; +} + +  #ifdef CONFIG_NL80211_TESTMODE  static struct genl_multicast_group nl80211_testmode_mcgrp = {  	.name = "testmode", @@ -5384,7 +6009,7 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)  	err = -EOPNOTSUPP;  	if (rdev->ops->testmode_cmd) {  		rdev->testmode_info = info; -		err = rdev->ops->testmode_cmd(&rdev->wiphy, +		err = rdev_testmode_cmd(rdev,  				nla_data(info->attrs[NL80211_ATTR_TESTDATA]),  				nla_len(info->attrs[NL80211_ATTR_TESTDATA]));  		rdev->testmode_info = NULL; @@ -5466,8 +6091,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,  			genlmsg_cancel(skb, hdr);  			break;  		} -		err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, -					       data, data_len); +		err = rdev_testmode_dump(rdev, skb, cb, data, data_len);  		nla_nest_end(skb, tmdata);  		if (err == -ENOBUFS || err == -ENOENT) { @@ -5596,7 +6220,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {  		connect.auth_type =  			nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); -		if (!nl80211_valid_auth_type(connect.auth_type)) +		if (!nl80211_valid_auth_type(rdev, connect.auth_type, +					     NL80211_CMD_CONNECT))  			return -EINVAL;  	} else  		connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; @@ -5631,6 +6256,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)  		connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);  	} +	if (info->attrs[NL80211_ATTR_USE_MFP]) { +		connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); +		if (connect.mfp != NL80211_MFP_REQUIRED && +		    connect.mfp != NL80211_MFP_NO) +			return -EINVAL; +	} else { +		connect.mfp = NL80211_MFP_NO; +	} +  	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {  		connect.channel =  			ieee80211_get_channel(wiphy, @@ -5642,7 +6276,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)  	if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {  		connkeys = nl80211_parse_connkeys(rdev, -					info->attrs[NL80211_ATTR_KEYS]); +					  info->attrs[NL80211_ATTR_KEYS], NULL);  		if (IS_ERR(connkeys))  			return PTR_ERR(connkeys);  	} @@ -5771,7 +6405,7 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)  	if (!rdev->ops->flush_pmksa)  		return -EOPNOTSUPP; -	return rdev->ops->flush_pmksa(&rdev->wiphy, dev); +	return rdev_flush_pmksa(rdev, dev);  }  static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) @@ -5798,10 +6432,10 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)  	status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);  	dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); -	return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, -				    dialog_token, status_code, -				    nla_data(info->attrs[NL80211_ATTR_IE]), -				    nla_len(info->attrs[NL80211_ATTR_IE])); +	return rdev_tdls_mgmt(rdev, dev, peer, action_code, +			      dialog_token, status_code, +			      nla_data(info->attrs[NL80211_ATTR_IE]), +			      nla_len(info->attrs[NL80211_ATTR_IE]));  }  static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) @@ -5822,7 +6456,7 @@ static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)  	operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);  	peer = nla_data(info->attrs[NL80211_ATTR_MAC]); -	return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); +	return rdev_tdls_oper(rdev, dev, peer, operation);  }  static int nl80211_remain_on_channel(struct sk_buff *skb, @@ -5830,12 +6464,11 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct wireless_dev *wdev = info->user_ptr[1]; -	struct ieee80211_channel *chan; +	struct cfg80211_chan_def chandef;  	struct sk_buff *msg;  	void *hdr;  	u64 cookie; -	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; -	u32 freq, duration; +	u32 duration;  	int err;  	if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || @@ -5856,14 +6489,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,  	    duration > rdev->wiphy.max_remain_on_channel_duration)  		return -EINVAL; -	if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && -	    !nl80211_valid_channel_type(info, &channel_type)) -		return -EINVAL; - -	freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); -	chan = rdev_freq_to_chan(rdev, freq, channel_type); -	if (chan == NULL) -		return -EINVAL; +	err = nl80211_parse_chandef(rdev, info, &chandef); +	if (err) +		return err;  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  	if (!msg) @@ -5877,8 +6505,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,  		goto free_msg;  	} -	err = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, -					   channel_type, duration, &cookie); +	err = rdev_remain_on_channel(rdev, wdev, chandef.chan, +				     duration, &cookie);  	if (err)  		goto free_msg; @@ -5912,7 +6540,7 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,  	cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); -	return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); +	return rdev_cancel_remain_on_channel(rdev, wdev, cookie);  }  static u32 rateset_to_mask(struct ieee80211_supported_band *sband, @@ -6055,7 +6683,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,  		}  	} -	return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); +	return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);  }  static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) @@ -6097,10 +6725,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct wireless_dev *wdev = info->user_ptr[1]; -	struct ieee80211_channel *chan; -	enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; -	bool channel_type_valid = false; -	u32 freq; +	struct cfg80211_chan_def chandef;  	int err;  	void *hdr = NULL;  	u64 cookie; @@ -6110,8 +6735,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  	dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; -	if (!info->attrs[NL80211_ATTR_FRAME] || -	    !info->attrs[NL80211_ATTR_WIPHY_FREQ]) +	if (!info->attrs[NL80211_ATTR_FRAME])  		return -EINVAL;  	if (!rdev->ops->mgmt_tx) @@ -6146,12 +6770,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  	} -	if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { -		if (!nl80211_valid_channel_type(info, &channel_type)) -			return -EINVAL; -		channel_type_valid = true; -	} -  	offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];  	if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) @@ -6159,10 +6777,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  	no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); -	freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); -	chan = rdev_freq_to_chan(rdev, freq, channel_type); -	if (chan == NULL) -		return -EINVAL; +	err = nl80211_parse_chandef(rdev, info, &chandef); +	if (err) +		return err;  	if (!dont_wait_for_ack) {  		msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -6178,8 +6795,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  		}  	} -	err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type, -				    channel_type_valid, wait, +	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); @@ -6230,7 +6846,7 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in  	cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); -	return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); +	return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie);  }  static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) @@ -6260,8 +6876,7 @@ static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)  	if (state == wdev->ps)  		return 0; -	err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state, -					wdev->ps_timeout); +	err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout);  	if (!err)  		wdev->ps = state;  	return err; @@ -6322,14 +6937,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {  };  static int nl80211_set_cqm_txe(struct genl_info *info, -				u32 rate, u32 pkts, u32 intvl) +			       u32 rate, u32 pkts, u32 intvl)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct wireless_dev *wdev;  	struct net_device *dev = info->user_ptr[1]; -	if ((rate < 0 || rate > 100) || -	    (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL)) +	if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)  		return -EINVAL;  	wdev = dev->ieee80211_ptr; @@ -6341,8 +6955,7 @@ static int nl80211_set_cqm_txe(struct genl_info *info,  	    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)  		return -EOPNOTSUPP; -	return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev, -					     rate, pkts, intvl); +	return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);  }  static int nl80211_set_cqm_rssi(struct genl_info *info, @@ -6364,8 +6977,7 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,  	    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)  		return -EOPNOTSUPP; -	return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, -					      threshold, hysteresis); +	return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);  }  static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) @@ -6438,6 +7050,21 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)  			    nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))  			return -EINVAL; +	if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { +		setup.beacon_interval = +			nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); +		if (setup.beacon_interval < 10 || +		    setup.beacon_interval > 10000) +			return -EINVAL; +	} + +	if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { +		setup.dtim_period = +			nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); +		if (setup.dtim_period < 1 || setup.dtim_period > 100) +			return -EINVAL; +	} +  	if (info->attrs[NL80211_ATTR_MESH_SETUP]) {  		/* parse additional setup parameters if given */  		err = nl80211_parse_mesh_setup(info, &setup); @@ -6446,21 +7073,12 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)  	}  	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { -		enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - -		if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && -		    !nl80211_valid_channel_type(info, &channel_type)) -			return -EINVAL; - -		setup.channel = rdev_freq_to_chan(rdev, -			nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), -			channel_type); -		if (!setup.channel) -			return -EINVAL; -		setup.channel_type = channel_type; +		err = nl80211_parse_chandef(rdev, info, &setup.chandef); +		if (err) +			return err;  	} else {  		/* cfg80211_join_mesh() will sort it out */ -		setup.channel = NULL; +		setup.chandef.chan = NULL;  	}  	return cfg80211_join_mesh(rdev, dev, &setup, &cfg); @@ -6475,16 +7093,100 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)  }  #ifdef CONFIG_PM +static int nl80211_send_wowlan_patterns(struct sk_buff *msg, +					struct cfg80211_registered_device *rdev) +{ +	struct nlattr *nl_pats, *nl_pat; +	int i, pat_len; + +	if (!rdev->wowlan->n_patterns) +		return 0; + +	nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN); +	if (!nl_pats) +		return -ENOBUFS; + +	for (i = 0; i < rdev->wowlan->n_patterns; i++) { +		nl_pat = nla_nest_start(msg, i + 1); +		if (!nl_pat) +			return -ENOBUFS; +		pat_len = rdev->wowlan->patterns[i].pattern_len; +		if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, +			    DIV_ROUND_UP(pat_len, 8), +			    rdev->wowlan->patterns[i].mask) || +		    nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, +			    pat_len, rdev->wowlan->patterns[i].pattern) || +		    nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET, +				rdev->wowlan->patterns[i].pkt_offset)) +			return -ENOBUFS; +		nla_nest_end(msg, nl_pat); +	} +	nla_nest_end(msg, nl_pats); + +	return 0; +} + +static int nl80211_send_wowlan_tcp(struct sk_buff *msg, +				   struct cfg80211_wowlan_tcp *tcp) +{ +	struct nlattr *nl_tcp; + +	if (!tcp) +		return 0; + +	nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION); +	if (!nl_tcp) +		return -ENOBUFS; + +	if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) || +	    nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) || +	    nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) || +	    nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) || +	    nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) || +	    nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, +		    tcp->payload_len, tcp->payload) || +	    nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL, +			tcp->data_interval) || +	    nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD, +		    tcp->wake_len, tcp->wake_data) || +	    nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK, +		    DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask)) +		return -ENOBUFS; + +	if (tcp->payload_seq.len && +	    nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, +		    sizeof(tcp->payload_seq), &tcp->payload_seq)) +		return -ENOBUFS; + +	if (tcp->payload_tok.len && +	    nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, +		    sizeof(tcp->payload_tok) + tcp->tokens_size, +		    &tcp->payload_tok)) +		return -ENOBUFS; + +	return 0; +} +  static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct sk_buff *msg;  	void *hdr; +	u32 size = NLMSG_DEFAULT_SIZE; -	if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) +	if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns && +	    !rdev->wiphy.wowlan.tcp)  		return -EOPNOTSUPP; -	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	if (rdev->wowlan && rdev->wowlan->tcp) { +		/* adjust size to have room for all the data */ +		size += rdev->wowlan->tcp->tokens_size + +			rdev->wowlan->tcp->payload_len + +			rdev->wowlan->tcp->wake_len + +			rdev->wowlan->tcp->wake_len / 8; +	} + +	msg = nlmsg_new(size, GFP_KERNEL);  	if (!msg)  		return -ENOMEM; @@ -6515,31 +7217,12 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)  		    (rdev->wowlan->rfkill_release &&  		     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))  			goto nla_put_failure; -		if (rdev->wowlan->n_patterns) { -			struct nlattr *nl_pats, *nl_pat; -			int i, pat_len; -			nl_pats = nla_nest_start(msg, -					NL80211_WOWLAN_TRIG_PKT_PATTERN); -			if (!nl_pats) -				goto nla_put_failure; +		if (nl80211_send_wowlan_patterns(msg, rdev)) +			goto nla_put_failure; -			for (i = 0; i < rdev->wowlan->n_patterns; i++) { -				nl_pat = nla_nest_start(msg, i + 1); -				if (!nl_pat) -					goto nla_put_failure; -				pat_len = rdev->wowlan->patterns[i].pattern_len; -				if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, -					    DIV_ROUND_UP(pat_len, 8), -					    rdev->wowlan->patterns[i].mask) || -				    nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, -					    pat_len, -					    rdev->wowlan->patterns[i].pattern)) -					goto nla_put_failure; -				nla_nest_end(msg, nl_pat); -			} -			nla_nest_end(msg, nl_pats); -		} +		if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp)) +			goto nla_put_failure;  		nla_nest_end(msg, nl_wowlan);  	} @@ -6552,6 +7235,150 @@ nla_put_failure:  	return -ENOBUFS;  } +static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, +				    struct nlattr *attr, +				    struct cfg80211_wowlan *trig) +{ +	struct nlattr *tb[NUM_NL80211_WOWLAN_TCP]; +	struct cfg80211_wowlan_tcp *cfg; +	struct nl80211_wowlan_tcp_data_token *tok = NULL; +	struct nl80211_wowlan_tcp_data_seq *seq = NULL; +	u32 size; +	u32 data_size, wake_size, tokens_size = 0, wake_mask_size; +	int err, port; + +	if (!rdev->wiphy.wowlan.tcp) +		return -EINVAL; + +	err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP, +			nla_data(attr), nla_len(attr), +			nl80211_wowlan_tcp_policy); +	if (err) +		return err; + +	if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] || +	    !tb[NL80211_WOWLAN_TCP_DST_IPV4] || +	    !tb[NL80211_WOWLAN_TCP_DST_MAC] || +	    !tb[NL80211_WOWLAN_TCP_DST_PORT] || +	    !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] || +	    !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] || +	    !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] || +	    !tb[NL80211_WOWLAN_TCP_WAKE_MASK]) +		return -EINVAL; + +	data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]); +	if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max) +		return -EINVAL; + +	if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) > +			rdev->wiphy.wowlan.tcp->data_interval_max) +		return -EINVAL; + +	wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]); +	if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max) +		return -EINVAL; + +	wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]); +	if (wake_mask_size != DIV_ROUND_UP(wake_size, 8)) +		return -EINVAL; + +	if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) { +		u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); + +		tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); +		tokens_size = tokln - sizeof(*tok); + +		if (!tok->len || tokens_size % tok->len) +			return -EINVAL; +		if (!rdev->wiphy.wowlan.tcp->tok) +			return -EINVAL; +		if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len) +			return -EINVAL; +		if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len) +			return -EINVAL; +		if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize) +			return -EINVAL; +		if (tok->offset + tok->len > data_size) +			return -EINVAL; +	} + +	if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) { +		seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]); +		if (!rdev->wiphy.wowlan.tcp->seq) +			return -EINVAL; +		if (seq->len == 0 || seq->len > 4) +			return -EINVAL; +		if (seq->len + seq->offset > data_size) +			return -EINVAL; +	} + +	size = sizeof(*cfg); +	size += data_size; +	size += wake_size + wake_mask_size; +	size += tokens_size; + +	cfg = kzalloc(size, GFP_KERNEL); +	if (!cfg) +		return -ENOMEM; +	cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]); +	cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]); +	memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]), +	       ETH_ALEN); +	if (tb[NL80211_WOWLAN_TCP_SRC_PORT]) +		port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]); +	else +		port = 0; +#ifdef CONFIG_INET +	/* allocate a socket and port for it and use it */ +	err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM, +			    IPPROTO_TCP, &cfg->sock, 1); +	if (err) { +		kfree(cfg); +		return err; +	} +	if (inet_csk_get_port(cfg->sock->sk, port)) { +		sock_release(cfg->sock); +		kfree(cfg); +		return -EADDRINUSE; +	} +	cfg->src_port = inet_sk(cfg->sock->sk)->inet_num; +#else +	if (!port) { +		kfree(cfg); +		return -EINVAL; +	} +	cfg->src_port = port; +#endif + +	cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]); +	cfg->payload_len = data_size; +	cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size; +	memcpy((void *)cfg->payload, +	       nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]), +	       data_size); +	if (seq) +		cfg->payload_seq = *seq; +	cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]); +	cfg->wake_len = wake_size; +	cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size; +	memcpy((void *)cfg->wake_data, +	       nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]), +	       wake_size); +	cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size + +			 data_size + wake_size; +	memcpy((void *)cfg->wake_mask, +	       nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]), +	       wake_mask_size); +	if (tok) { +		cfg->tokens_size = tokens_size; +		memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size); +	} + +	trig->tcp = cfg; + +	return 0; +} +  static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6562,7 +7389,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)  	int err, i;  	bool prev_enabled = rdev->wowlan; -	if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) +	if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns && +	    !rdev->wiphy.wowlan.tcp)  		return -EOPNOTSUPP;  	if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { @@ -6626,7 +7454,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)  	if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {  		struct nlattr *pat;  		int n_patterns = 0; -		int rem, pat_len, mask_len; +		int rem, pat_len, mask_len, pkt_offset;  		struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];  		nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], @@ -6661,6 +7489,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)  			    pat_len < wowlan->pattern_min_len)  				goto error; +			if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]) +				pkt_offset = 0; +			else +				pkt_offset = nla_get_u32( +					pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]); +			if (pkt_offset > wowlan->max_pkt_offset) +				goto error; +			new_triggers.patterns[i].pkt_offset = pkt_offset; +  			new_triggers.patterns[i].mask =  				kmalloc(mask_len + pat_len, GFP_KERNEL);  			if (!new_triggers.patterns[i].mask) { @@ -6680,6 +7517,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)  		}  	} +	if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) { +		err = nl80211_parse_wowlan_tcp( +			rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION], +			&new_triggers); +		if (err) +			goto error; +	} +  	ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);  	if (!ntrig) {  		err = -ENOMEM; @@ -6690,13 +7535,16 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)   set_wakeup:  	if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan) -		rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan); +		rdev_set_wakeup(rdev, rdev->wowlan);  	return 0;   error:  	for (i = 0; i < new_triggers.n_patterns; i++)  		kfree(new_triggers.patterns[i].mask);  	kfree(new_triggers.patterns); +	if (new_triggers.tcp && new_triggers.tcp->sock) +		sock_release(new_triggers.tcp->sock); +	kfree(new_triggers.tcp);  	return err;  }  #endif @@ -6746,7 +7594,7 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)  		goto out;  	} -	err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); +	err = rdev_set_rekey_data(rdev, dev, &rekey_data);   out:  	wdev_unlock(wdev);  	return err; @@ -6805,7 +7653,7 @@ static int nl80211_probe_client(struct sk_buff *skb,  	addr = nla_data(info->attrs[NL80211_ATTR_MAC]); -	err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); +	err = rdev_probe_client(rdev, dev, addr, &cookie);  	if (err)  		goto free_msg; @@ -6826,16 +7674,35 @@ static int nl80211_probe_client(struct sk_buff *skb,  static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; +	struct cfg80211_beacon_registration *reg, *nreg; +	int rv;  	if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))  		return -EOPNOTSUPP; -	if (rdev->ap_beacons_nlportid) -		return -EBUSY; +	nreg = kzalloc(sizeof(*nreg), GFP_KERNEL); +	if (!nreg) +		return -ENOMEM; -	rdev->ap_beacons_nlportid = info->snd_portid; +	/* First, check if already registered. */ +	spin_lock_bh(&rdev->beacon_registrations_lock); +	list_for_each_entry(reg, &rdev->beacon_registrations, list) { +		if (reg->nlportid == info->snd_portid) { +			rv = -EALREADY; +			goto out_err; +		} +	} +	/* Add it to the list */ +	nreg->nlportid = info->snd_portid; +	list_add(&nreg->list, &rdev->beacon_registrations); + +	spin_unlock_bh(&rdev->beacon_registrations_lock);  	return 0; +out_err: +	spin_unlock_bh(&rdev->beacon_registrations_lock); +	kfree(nreg); +	return rv;  }  static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) @@ -6859,7 +7726,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)  	if (err)  		return err; -	err = rdev->ops->start_p2p_device(&rdev->wiphy, wdev); +	err = rdev_start_p2p_device(rdev, wdev);  	if (err)  		return err; @@ -6885,7 +7752,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)  	if (!wdev->p2p_started)  		return 0; -	rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); +	rdev_stop_p2p_device(rdev, wdev);  	wdev->p2p_started = false;  	mutex_lock(&rdev->devlist_mtx); @@ -7552,6 +8419,30 @@ static struct genl_ops nl80211_ops[] = {  		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |  				  NL80211_FLAG_NEED_RTNL,  	}, +	{ +		.cmd = NL80211_CMD_SET_MCAST_RATE, +		.doit = nl80211_set_mcast_rate, +		.policy = nl80211_policy, +		.flags = GENL_ADMIN_PERM, +		.internal_flags = NL80211_FLAG_NEED_NETDEV | +				  NL80211_FLAG_NEED_RTNL, +	}, +	{ +		.cmd = NL80211_CMD_SET_MAC_ACL, +		.doit = nl80211_set_mac_acl, +		.policy = nl80211_policy, +		.flags = GENL_ADMIN_PERM, +		.internal_flags = NL80211_FLAG_NEED_NETDEV | +				  NL80211_FLAG_NEED_RTNL, +	}, +	{ +		.cmd = NL80211_CMD_RADAR_DETECT, +		.doit = nl80211_start_radar_detection, +		.policy = nl80211_policy, +		.flags = GENL_ADMIN_PERM, +		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP | +				  NL80211_FLAG_NEED_RTNL, +	},  };  static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -7622,6 +8513,9 @@ static int nl80211_add_scan_req(struct sk_buff *msg,  	    nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))  		goto nla_put_failure; +	if (req->flags) +		nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags); +  	return 0;   nla_put_failure:  	return -ENOBUFS; @@ -7816,7 +8710,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)  			goto nla_put_failure;  	} -	if (wiphy_idx_valid(request->wiphy_idx) && +	if (request->wiphy_idx != WIPHY_IDX_INVALID &&  	    nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))  		goto nla_put_failure; @@ -8250,7 +9144,6 @@ static void nl80211_send_remain_on_chan_event(  	int cmd, struct cfg80211_registered_device *rdev,  	struct wireless_dev *wdev, u64 cookie,  	struct ieee80211_channel *chan, -	enum nl80211_channel_type channel_type,  	unsigned int duration, gfp_t gfp)  {  	struct sk_buff *msg; @@ -8271,7 +9164,8 @@ static void nl80211_send_remain_on_chan_event(  					 wdev->netdev->ifindex)) ||  	    nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||  	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || -	    nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || +	    nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, +			NL80211_CHAN_NO_HT) ||  	    nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))  		goto nla_put_failure; @@ -8293,23 +9187,20 @@ static void nl80211_send_remain_on_chan_event(  void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,  				    struct wireless_dev *wdev, u64 cookie,  				    struct ieee80211_channel *chan, -				    enum nl80211_channel_type channel_type,  				    unsigned int duration, gfp_t gfp)  {  	nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,  					  rdev, wdev, cookie, chan, -					  channel_type, duration, gfp); +					  duration, gfp);  }  void nl80211_send_remain_on_channel_cancel(  	struct cfg80211_registered_device *rdev,  	struct wireless_dev *wdev, -	u64 cookie, struct ieee80211_channel *chan, -	enum nl80211_channel_type channel_type, gfp_t gfp) +	u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)  {  	nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, -					  rdev, wdev, cookie, chan, -					  channel_type, 0, gfp); +					  rdev, wdev, cookie, chan, 0, gfp);  }  void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, @@ -8665,8 +9556,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,  }  void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, -			      struct net_device *netdev, int freq, -			      enum nl80211_channel_type type, gfp_t gfp) +			      struct net_device *netdev, +			      struct cfg80211_chan_def *chandef, gfp_t gfp)  {  	struct sk_buff *msg;  	void *hdr; @@ -8681,9 +9572,10 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,  		return;  	} -	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || -	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || -	    nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type)) +	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) +		goto nla_put_failure; + +	if (nl80211_send_chandef(msg, chandef))  		goto nla_put_failure;  	genlmsg_end(msg, hdr); @@ -8748,6 +9640,57 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,  }  void +nl80211_radar_notify(struct cfg80211_registered_device *rdev, +		     struct cfg80211_chan_def *chandef, +		     enum nl80211_radar_event event, +		     struct net_device *netdev, gfp_t gfp) +{ +	struct sk_buff *msg; +	void *hdr; + +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); +	if (!msg) +		return; + +	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT); +	if (!hdr) { +		nlmsg_free(msg); +		return; +	} + +	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) +		goto nla_put_failure; + +	/* NOP and radar events don't need a netdev parameter */ +	if (netdev) { +		struct wireless_dev *wdev = netdev->ieee80211_ptr; + +		if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || +		    nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) +			goto nla_put_failure; +	} + +	if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event)) +		goto nla_put_failure; + +	if (nl80211_send_chandef(msg, chandef)) +		goto nla_put_failure; + +	if (genlmsg_end(msg, hdr) < 0) { +		nlmsg_free(msg); +		return; +	} + +	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, +				nl80211_mlme_mcgrp.id, gfp); +	return; + + nla_put_failure: +	genlmsg_cancel(msg, hdr); +	nlmsg_free(msg); +} + +void  nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,  				struct net_device *netdev, const u8 *peer,  				u32 num_packets, gfp_t gfp) @@ -8800,7 +9743,10 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,  	void *hdr;  	int err; +	trace_cfg80211_probe_status(dev, addr, cookie, acked); +  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); +  	if (!msg)  		return; @@ -8835,44 +9781,204 @@ EXPORT_SYMBOL(cfg80211_probe_status);  void cfg80211_report_obss_beacon(struct wiphy *wiphy,  				 const u8 *frame, size_t len, -				 int freq, int sig_dbm, gfp_t gfp) +				 int freq, int sig_dbm)  {  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);  	struct sk_buff *msg;  	void *hdr; -	u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid); +	struct cfg80211_beacon_registration *reg; -	if (!nlportid) +	trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm); + +	spin_lock_bh(&rdev->beacon_registrations_lock); +	list_for_each_entry(reg, &rdev->beacon_registrations, list) { +		msg = nlmsg_new(len + 100, GFP_ATOMIC); +		if (!msg) { +			spin_unlock_bh(&rdev->beacon_registrations_lock); +			return; +		} + +		hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); +		if (!hdr) +			goto nla_put_failure; + +		if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || +		    (freq && +		     nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || +		    (sig_dbm && +		     nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || +		    nla_put(msg, NL80211_ATTR_FRAME, len, frame)) +			goto nla_put_failure; + +		genlmsg_end(msg, hdr); + +		genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid); +	} +	spin_unlock_bh(&rdev->beacon_registrations_lock); +	return; + + nla_put_failure: +	spin_unlock_bh(&rdev->beacon_registrations_lock); +	if (hdr) +		genlmsg_cancel(msg, hdr); +	nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_report_obss_beacon); + +#ifdef CONFIG_PM +void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, +				   struct cfg80211_wowlan_wakeup *wakeup, +				   gfp_t gfp) +{ +	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct sk_buff *msg; +	void *hdr; +	int err, size = 200; + +	trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup); + +	if (wakeup) +		size += wakeup->packet_present_len; + +	msg = nlmsg_new(size, gfp); +	if (!msg)  		return; -	msg = nlmsg_new(len + 100, gfp); +	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN); +	if (!hdr) +		goto free_msg; + +	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || +	    nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) +		goto free_msg; + +	if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, +					wdev->netdev->ifindex)) +		goto free_msg; + +	if (wakeup) { +		struct nlattr *reasons; + +		reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); + +		if (wakeup->disconnect && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) +			goto free_msg; +		if (wakeup->magic_pkt && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) +			goto free_msg; +		if (wakeup->gtk_rekey_failure && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) +			goto free_msg; +		if (wakeup->eap_identity_req && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) +			goto free_msg; +		if (wakeup->four_way_handshake && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) +			goto free_msg; +		if (wakeup->rfkill_release && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)) +			goto free_msg; + +		if (wakeup->pattern_idx >= 0 && +		    nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, +				wakeup->pattern_idx)) +			goto free_msg; + +		if (wakeup->tcp_match) +			nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH); + +		if (wakeup->tcp_connlost) +			nla_put_flag(msg, +				     NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST); + +		if (wakeup->tcp_nomoretokens) +			nla_put_flag(msg, +				NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS); + +		if (wakeup->packet) { +			u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; +			u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN; + +			if (!wakeup->packet_80211) { +				pkt_attr = +					NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023; +				len_attr = +					NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN; +			} + +			if (wakeup->packet_len && +			    nla_put_u32(msg, len_attr, wakeup->packet_len)) +				goto free_msg; + +			if (nla_put(msg, pkt_attr, wakeup->packet_present_len, +				    wakeup->packet)) +				goto free_msg; +		} + +		nla_nest_end(msg, reasons); +	} + +	err = genlmsg_end(msg, hdr); +	if (err < 0) +		goto free_msg; + +	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, +				nl80211_mlme_mcgrp.id, gfp); +	return; + + free_msg: +	nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup); +#endif + +void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, +				enum nl80211_tdls_operation oper, +				u16 reason_code, gfp_t gfp) +{ +	struct wireless_dev *wdev = dev->ieee80211_ptr; +	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct sk_buff *msg; +	void *hdr; +	int err; + +	trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper, +					 reason_code); + +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);  	if (!msg)  		return; -	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); +	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);  	if (!hdr) {  		nlmsg_free(msg);  		return;  	}  	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || -	    (freq && -	     nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || -	    (sig_dbm && -	     nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || -	    nla_put(msg, NL80211_ATTR_FRAME, len, frame)) +	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || +	    nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) || +	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) || +	    (reason_code > 0 && +	     nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))  		goto nla_put_failure; -	genlmsg_end(msg, hdr); +	err = genlmsg_end(msg, hdr); +	if (err < 0) { +		nlmsg_free(msg); +		return; +	} -	genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); +	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, +				nl80211_mlme_mcgrp.id, gfp);  	return;   nla_put_failure:  	genlmsg_cancel(msg, hdr);  	nlmsg_free(msg);  } -EXPORT_SYMBOL(cfg80211_report_obss_beacon); +EXPORT_SYMBOL(cfg80211_tdls_oper_request);  static int nl80211_netlink_notify(struct notifier_block * nb,  				  unsigned long state, @@ -8881,6 +9987,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,  	struct netlink_notify *notify = _notify;  	struct cfg80211_registered_device *rdev;  	struct wireless_dev *wdev; +	struct cfg80211_beacon_registration *reg, *tmp;  	if (state != NETLINK_URELEASE)  		return NOTIFY_DONE; @@ -8890,8 +9997,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb,  	list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {  		list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)  			cfg80211_mlme_unregister_socket(wdev, notify->portid); -		if (rdev->ap_beacons_nlportid == notify->portid) -			rdev->ap_beacons_nlportid = 0; + +		spin_lock_bh(&rdev->beacon_registrations_lock); +		list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, +					 list) { +			if (reg->nlportid == notify->portid) { +				list_del(®->list); +				kfree(reg); +				break; +			} +		} +		spin_unlock_bh(&rdev->beacon_registrations_lock);  	}  	rcu_read_unlock(); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f6153516068..b061da4919e 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -76,13 +76,11 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,  void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,  				    struct wireless_dev *wdev, u64 cookie,  				    struct ieee80211_channel *chan, -				    enum nl80211_channel_type channel_type,  				    unsigned int duration, gfp_t gfp);  void nl80211_send_remain_on_channel_cancel(  	struct cfg80211_registered_device *rdev,  	struct wireless_dev *wdev, -	u64 cookie, struct ieee80211_channel *chan, -	enum nl80211_channel_type channel_type, gfp_t gfp); +	u64 cookie, struct ieee80211_channel *chan, gfp_t gfp);  void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,  			    struct net_device *dev, const u8 *mac_addr, @@ -110,6 +108,13 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,  			     struct net_device *netdev,  			     enum nl80211_cqm_rssi_threshold_event rssi_event,  			     gfp_t gfp); + +void +nl80211_radar_notify(struct cfg80211_registered_device *rdev, +		     struct cfg80211_chan_def *chandef, +		     enum nl80211_radar_event event, +		     struct net_device *netdev, gfp_t gfp); +  void  nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,  				struct net_device *netdev, const u8 *peer, @@ -129,8 +134,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,  				    const u8 *bssid, bool preauth, gfp_t gfp);  void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, -			      struct net_device *dev, int freq, -			      enum nl80211_channel_type type, gfp_t gfp); +			      struct net_device *dev, +			      struct cfg80211_chan_def *chandef, gfp_t gfp);  bool nl80211_unexpected_frame(struct net_device *dev,  			      const u8 *addr, gfp_t gfp); diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h new file mode 100644 index 00000000000..422d38291d6 --- /dev/null +++ b/net/wireless/rdev-ops.h @@ -0,0 +1,890 @@ +#ifndef __CFG80211_RDEV_OPS +#define __CFG80211_RDEV_OPS + +#include <linux/rtnetlink.h> +#include <net/cfg80211.h> +#include "core.h" +#include "trace.h" + +static inline int rdev_suspend(struct cfg80211_registered_device *rdev) +{ +	int ret; +	trace_rdev_suspend(&rdev->wiphy, rdev->wowlan); +	ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_resume(struct cfg80211_registered_device *rdev) +{ +	int ret; +	trace_rdev_resume(&rdev->wiphy); +	ret = rdev->ops->resume(&rdev->wiphy); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev, +				   bool enabled) +{ +	trace_rdev_set_wakeup(&rdev->wiphy, enabled); +	rdev->ops->set_wakeup(&rdev->wiphy, enabled); +	trace_rdev_return_void(&rdev->wiphy); +} + +static inline struct wireless_dev +*rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name, +		       enum nl80211_iftype type, u32 *flags, +		       struct vif_params *params) +{ +	struct wireless_dev *ret; +	trace_rdev_add_virtual_intf(&rdev->wiphy, name, type); +	ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, type, flags, +					  params); +	trace_rdev_return_wdev(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_del_virtual_intf(struct cfg80211_registered_device *rdev, +		      struct wireless_dev *wdev) +{ +	int ret; +	trace_rdev_del_virtual_intf(&rdev->wiphy, wdev); +	ret = rdev->ops->del_virtual_intf(&rdev->wiphy, wdev); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_change_virtual_intf(struct cfg80211_registered_device *rdev, +			 struct net_device *dev, enum nl80211_iftype type, +			 u32 *flags, struct vif_params *params) +{ +	int ret; +	trace_rdev_change_virtual_intf(&rdev->wiphy, dev, type); +	ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, flags, +					     params); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_add_key(struct cfg80211_registered_device *rdev, +			       struct net_device *netdev, u8 key_index, +			       bool pairwise, const u8 *mac_addr, +			       struct key_params *params) +{ +	int ret; +	trace_rdev_add_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr); +	ret = rdev->ops->add_key(&rdev->wiphy, netdev, key_index, pairwise, +				  mac_addr, params); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_get_key(struct cfg80211_registered_device *rdev, struct net_device *netdev, +	     u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie, +	     void (*callback)(void *cookie, struct key_params*)) +{ +	int ret; +	trace_rdev_get_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr); +	ret = rdev->ops->get_key(&rdev->wiphy, netdev, key_index, pairwise, +				  mac_addr, cookie, callback); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_del_key(struct cfg80211_registered_device *rdev, +			       struct net_device *netdev, u8 key_index, +			       bool pairwise, const u8 *mac_addr) +{ +	int ret; +	trace_rdev_del_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr); +	ret = rdev->ops->del_key(&rdev->wiphy, netdev, key_index, pairwise, +				  mac_addr); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_set_default_key(struct cfg80211_registered_device *rdev, +		     struct net_device *netdev, u8 key_index, bool unicast, +		     bool multicast) +{ +	int ret; +	trace_rdev_set_default_key(&rdev->wiphy, netdev, key_index, +				   unicast, multicast); +	ret = rdev->ops->set_default_key(&rdev->wiphy, netdev, key_index, +					  unicast, multicast); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev, +			  struct net_device *netdev, u8 key_index) +{ +	int ret; +	trace_rdev_set_default_mgmt_key(&rdev->wiphy, netdev, key_index); +	ret = rdev->ops->set_default_mgmt_key(&rdev->wiphy, netdev, +					       key_index); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_start_ap(struct cfg80211_registered_device *rdev, +				struct net_device *dev, +				struct cfg80211_ap_settings *settings) +{ +	int ret; +	trace_rdev_start_ap(&rdev->wiphy, dev, settings); +	ret = rdev->ops->start_ap(&rdev->wiphy, dev, settings); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_change_beacon(struct cfg80211_registered_device *rdev, +				     struct net_device *dev, +				     struct cfg80211_beacon_data *info) +{ +	int ret; +	trace_rdev_change_beacon(&rdev->wiphy, dev, info); +	ret = rdev->ops->change_beacon(&rdev->wiphy, dev, info); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_stop_ap(struct cfg80211_registered_device *rdev, +			       struct net_device *dev) +{ +	int ret; +	trace_rdev_stop_ap(&rdev->wiphy, dev); +	ret = rdev->ops->stop_ap(&rdev->wiphy, dev); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_add_station(struct cfg80211_registered_device *rdev, +				   struct net_device *dev, u8 *mac, +				   struct station_parameters *params) +{ +	int ret; +	trace_rdev_add_station(&rdev->wiphy, dev, mac, params); +	ret = rdev->ops->add_station(&rdev->wiphy, dev, mac, params); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_del_station(struct cfg80211_registered_device *rdev, +				   struct net_device *dev, u8 *mac) +{ +	int ret; +	trace_rdev_del_station(&rdev->wiphy, dev, mac); +	ret = rdev->ops->del_station(&rdev->wiphy, dev, mac); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_change_station(struct cfg80211_registered_device *rdev, +				      struct net_device *dev, u8 *mac, +				      struct station_parameters *params) +{ +	int ret; +	trace_rdev_change_station(&rdev->wiphy, dev, mac, params); +	ret = rdev->ops->change_station(&rdev->wiphy, dev, mac, params); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_get_station(struct cfg80211_registered_device *rdev, +				   struct net_device *dev, u8 *mac, +				   struct station_info *sinfo) +{ +	int ret; +	trace_rdev_get_station(&rdev->wiphy, dev, mac); +	ret = rdev->ops->get_station(&rdev->wiphy, dev, mac, sinfo); +	trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo); +	return ret; +} + +static inline int rdev_dump_station(struct cfg80211_registered_device *rdev, +				    struct net_device *dev, int idx, u8 *mac, +				    struct station_info *sinfo) +{ +	int ret; +	trace_rdev_dump_station(&rdev->wiphy, dev, idx, mac); +	ret = rdev->ops->dump_station(&rdev->wiphy, dev, idx, mac, sinfo); +	trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo); +	return ret; +} + +static inline int rdev_add_mpath(struct cfg80211_registered_device *rdev, +				 struct net_device *dev, u8 *dst, u8 *next_hop) +{ +	int ret; +	trace_rdev_add_mpath(&rdev->wiphy, dev, dst, next_hop); +	ret = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_del_mpath(struct cfg80211_registered_device *rdev, +				 struct net_device *dev, u8 *dst) +{ +	int ret; +	trace_rdev_del_mpath(&rdev->wiphy, dev, dst); +	ret = rdev->ops->del_mpath(&rdev->wiphy, dev, dst); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_change_mpath(struct cfg80211_registered_device *rdev, +				    struct net_device *dev, u8 *dst, +				    u8 *next_hop) +{ +	int ret; +	trace_rdev_change_mpath(&rdev->wiphy, dev, dst, next_hop); +	ret = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev, +				 struct net_device *dev, u8 *dst, u8 *next_hop, +				 struct mpath_info *pinfo) +{ +	int ret; +	trace_rdev_get_mpath(&rdev->wiphy, dev, dst, next_hop); +	ret = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, pinfo); +	trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); +	return ret; + +} + +static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev, +				  struct net_device *dev, int idx, u8 *dst, +				  u8 *next_hop, struct mpath_info *pinfo) + +{ +	int ret; +	trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop); +	ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop, +				     pinfo); +	trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); +	return ret; +} + +static inline int +rdev_get_mesh_config(struct cfg80211_registered_device *rdev, +		     struct net_device *dev, struct mesh_config *conf) +{ +	int ret; +	trace_rdev_get_mesh_config(&rdev->wiphy, dev); +	ret = rdev->ops->get_mesh_config(&rdev->wiphy, dev, conf); +	trace_rdev_return_int_mesh_config(&rdev->wiphy, ret, conf); +	return ret; +} + +static inline int +rdev_update_mesh_config(struct cfg80211_registered_device *rdev, +			struct net_device *dev, u32 mask, +			const struct mesh_config *nconf) +{ +	int ret; +	trace_rdev_update_mesh_config(&rdev->wiphy, dev, mask, nconf); +	ret = rdev->ops->update_mesh_config(&rdev->wiphy, dev, mask, nconf); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_join_mesh(struct cfg80211_registered_device *rdev, +				 struct net_device *dev, +				 const struct mesh_config *conf, +				 const struct mesh_setup *setup) +{ +	int ret; +	trace_rdev_join_mesh(&rdev->wiphy, dev, conf, setup); +	ret = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + + +static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev, +				  struct net_device *dev) +{ +	int ret; +	trace_rdev_leave_mesh(&rdev->wiphy, dev); +	ret = rdev->ops->leave_mesh(&rdev->wiphy, dev); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_change_bss(struct cfg80211_registered_device *rdev, +				  struct net_device *dev, +				  struct bss_parameters *params) + +{ +	int ret; +	trace_rdev_change_bss(&rdev->wiphy, dev, params); +	ret = rdev->ops->change_bss(&rdev->wiphy, dev, params); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_set_txq_params(struct cfg80211_registered_device *rdev, +				      struct net_device *dev, +				      struct ieee80211_txq_params *params) + +{ +	int ret; +	trace_rdev_set_txq_params(&rdev->wiphy, dev, params); +	ret = rdev->ops->set_txq_params(&rdev->wiphy, dev, params); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev, +			       struct net_device *dev, +			       struct ieee80211_channel *chan) +{ +	int ret; +	trace_rdev_libertas_set_mesh_channel(&rdev->wiphy, dev, chan); +	ret = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, dev, chan); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_set_monitor_channel(struct cfg80211_registered_device *rdev, +			 struct cfg80211_chan_def *chandef) +{ +	int ret; +	trace_rdev_set_monitor_channel(&rdev->wiphy, chandef); +	ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_scan(struct cfg80211_registered_device *rdev, +			    struct cfg80211_scan_request *request) +{ +	int ret; +	trace_rdev_scan(&rdev->wiphy, request); +	ret = rdev->ops->scan(&rdev->wiphy, request); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_auth(struct cfg80211_registered_device *rdev, +			    struct net_device *dev, +			    struct cfg80211_auth_request *req) +{ +	int ret; +	trace_rdev_auth(&rdev->wiphy, dev, req); +	ret = rdev->ops->auth(&rdev->wiphy, dev, req); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_assoc(struct cfg80211_registered_device *rdev, +			     struct net_device *dev, +			     struct cfg80211_assoc_request *req) +{ +	int ret; +	trace_rdev_assoc(&rdev->wiphy, dev, req); +	ret = rdev->ops->assoc(&rdev->wiphy, dev, req); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_deauth(struct cfg80211_registered_device *rdev, +			      struct net_device *dev, +			      struct cfg80211_deauth_request *req) +{ +	int ret; +	trace_rdev_deauth(&rdev->wiphy, dev, req); +	ret = rdev->ops->deauth(&rdev->wiphy, dev, req); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_disassoc(struct cfg80211_registered_device *rdev, +				struct net_device *dev, +				struct cfg80211_disassoc_request *req) +{ +	int ret; +	trace_rdev_disassoc(&rdev->wiphy, dev, req); +	ret = rdev->ops->disassoc(&rdev->wiphy, dev, req); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_connect(struct cfg80211_registered_device *rdev, +			       struct net_device *dev, +			       struct cfg80211_connect_params *sme) +{ +	int ret; +	trace_rdev_connect(&rdev->wiphy, dev, sme); +	ret = rdev->ops->connect(&rdev->wiphy, dev, sme); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_disconnect(struct cfg80211_registered_device *rdev, +				  struct net_device *dev, u16 reason_code) +{ +	int ret; +	trace_rdev_disconnect(&rdev->wiphy, dev, reason_code); +	ret = rdev->ops->disconnect(&rdev->wiphy, dev, reason_code); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_join_ibss(struct cfg80211_registered_device *rdev, +				 struct net_device *dev, +				 struct cfg80211_ibss_params *params) +{ +	int ret; +	trace_rdev_join_ibss(&rdev->wiphy, dev, params); +	ret = rdev->ops->join_ibss(&rdev->wiphy, dev, params); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_leave_ibss(struct cfg80211_registered_device *rdev, +				  struct net_device *dev) +{ +	int ret; +	trace_rdev_leave_ibss(&rdev->wiphy, dev); +	ret = rdev->ops->leave_ibss(&rdev->wiphy, dev); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed) +{ +	int ret; +	trace_rdev_set_wiphy_params(&rdev->wiphy, changed); +	ret = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev, +				    struct wireless_dev *wdev, +				    enum nl80211_tx_power_setting type, int mbm) +{ +	int ret; +	trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm); +	ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev, +				    struct wireless_dev *wdev, int *dbm) +{ +	int ret; +	trace_rdev_get_tx_power(&rdev->wiphy, wdev); +	ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm); +	trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm); +	return ret; +} + +static inline int rdev_set_wds_peer(struct cfg80211_registered_device *rdev, +				    struct net_device *dev, const u8 *addr) +{ +	int ret; +	trace_rdev_set_wds_peer(&rdev->wiphy, dev, addr); +	ret = rdev->ops->set_wds_peer(&rdev->wiphy, dev, addr); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) +{ +	trace_rdev_rfkill_poll(&rdev->wiphy); +	rdev->ops->rfkill_poll(&rdev->wiphy); +	trace_rdev_return_void(&rdev->wiphy); +} + + +#ifdef CONFIG_NL80211_TESTMODE +static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev, +				    void *data, int len) +{ +	int ret; +	trace_rdev_testmode_cmd(&rdev->wiphy); +	ret = rdev->ops->testmode_cmd(&rdev->wiphy, data, len); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_testmode_dump(struct cfg80211_registered_device *rdev, +				     struct sk_buff *skb, +				     struct netlink_callback *cb, void *data, +				     int len) +{ +	int ret; +	trace_rdev_testmode_dump(&rdev->wiphy); +	ret = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, data, len); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} +#endif + +static inline int +rdev_set_bitrate_mask(struct cfg80211_registered_device *rdev, +		      struct net_device *dev, const u8 *peer, +		      const struct cfg80211_bitrate_mask *mask) +{ +	int ret; +	trace_rdev_set_bitrate_mask(&rdev->wiphy, dev, peer, mask); +	ret = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, peer, mask); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_dump_survey(struct cfg80211_registered_device *rdev, +				   struct net_device *netdev, int idx, +				   struct survey_info *info) +{ +	int ret; +	trace_rdev_dump_survey(&rdev->wiphy, netdev, idx); +	ret = rdev->ops->dump_survey(&rdev->wiphy, netdev, idx, info); +	if (ret < 0) +		trace_rdev_return_int(&rdev->wiphy, ret); +	else +		trace_rdev_return_int_survey_info(&rdev->wiphy, ret, info); +	return ret; +} + +static inline int rdev_set_pmksa(struct cfg80211_registered_device *rdev, +				 struct net_device *netdev, +				 struct cfg80211_pmksa *pmksa) +{ +	int ret; +	trace_rdev_set_pmksa(&rdev->wiphy, netdev, pmksa); +	ret = rdev->ops->set_pmksa(&rdev->wiphy, netdev, pmksa); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_del_pmksa(struct cfg80211_registered_device *rdev, +				 struct net_device *netdev, +				 struct cfg80211_pmksa *pmksa) +{ +	int ret; +	trace_rdev_del_pmksa(&rdev->wiphy, netdev, pmksa); +	ret = rdev->ops->del_pmksa(&rdev->wiphy, netdev, pmksa); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_flush_pmksa(struct cfg80211_registered_device *rdev, +				   struct net_device *netdev) +{ +	int ret; +	trace_rdev_flush_pmksa(&rdev->wiphy, netdev); +	ret = rdev->ops->flush_pmksa(&rdev->wiphy, netdev); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_remain_on_channel(struct cfg80211_registered_device *rdev, +		       struct wireless_dev *wdev, +		       struct ieee80211_channel *chan, +		       unsigned int duration, u64 *cookie) +{ +	int ret; +	trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration); +	ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, +					   duration, cookie); +	trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); +	return ret; +} + +static inline int +rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev, +			      struct wireless_dev *wdev, u64 cookie) +{ +	int ret; +	trace_rdev_cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); +	ret = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +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) +{ +	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_return_int_cookie(&rdev->wiphy, ret, *cookie); +	return ret; +} + +static inline int +rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev, +			 struct wireless_dev *wdev, u64 cookie) +{ +	int ret; +	trace_rdev_mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); +	ret = rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_set_power_mgmt(struct cfg80211_registered_device *rdev, +				      struct net_device *dev, bool enabled, +				      int timeout) +{ +	int ret; +	trace_rdev_set_power_mgmt(&rdev->wiphy, dev, enabled, timeout); +	ret = rdev->ops->set_power_mgmt(&rdev->wiphy, dev, enabled, timeout); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev, +			 struct net_device *dev, s32 rssi_thold, u32 rssi_hyst) +{ +	int ret; +	trace_rdev_set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold, +				       rssi_hyst); +	ret = rdev->ops->set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold, +				       rssi_hyst); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev, +			struct net_device *dev, u32 rate, u32 pkts, u32 intvl) +{ +	int ret; +	trace_rdev_set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts, intvl); +	ret = rdev->ops->set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts, +					     intvl); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline void +rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev, +			 struct wireless_dev *wdev, u16 frame_type, bool reg) +{ +	trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg); +	rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg); +	trace_rdev_return_void(&rdev->wiphy); +} + +static inline int rdev_set_antenna(struct cfg80211_registered_device *rdev, +				   u32 tx_ant, u32 rx_ant) +{ +	int ret; +	trace_rdev_set_antenna(&rdev->wiphy, tx_ant, rx_ant); +	ret = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev, +				   u32 *tx_ant, u32 *rx_ant) +{ +	int ret; +	trace_rdev_get_antenna(&rdev->wiphy); +	ret = rdev->ops->get_antenna(&rdev->wiphy, tx_ant, rx_ant); +	if (ret) +		trace_rdev_return_int(&rdev->wiphy, ret); +	else +		trace_rdev_return_int_tx_rx(&rdev->wiphy, ret, *tx_ant, +					    *rx_ant); +	return ret; +} + +static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev, +				     u32 tx, u32 rx) +{ +	int ret; +	trace_rdev_set_ringparam(&rdev->wiphy, tx, rx); +	ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev, +				      u32 *tx, u32 *tx_max, u32 *rx, +				      u32 *rx_max) +{ +	trace_rdev_get_ringparam(&rdev->wiphy); +	rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max); +	trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max); +} + +static inline int +rdev_sched_scan_start(struct cfg80211_registered_device *rdev, +		      struct net_device *dev, +		      struct cfg80211_sched_scan_request *request) +{ +	int ret; +	trace_rdev_sched_scan_start(&rdev->wiphy, dev, request); +	ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_sched_scan_stop(struct cfg80211_registered_device *rdev, +				       struct net_device *dev) +{ +	int ret; +	trace_rdev_sched_scan_stop(&rdev->wiphy, dev); +	ret = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev, +				      struct net_device *dev, +				      struct cfg80211_gtk_rekey_data *data) +{ +	int ret; +	trace_rdev_set_rekey_data(&rdev->wiphy, dev); +	ret = rdev->ops->set_rekey_data(&rdev->wiphy, dev, data); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev, +				 struct net_device *dev, u8 *peer, +				 u8 action_code, u8 dialog_token, +				 u16 status_code, const u8 *buf, size_t len) +{ +	int ret; +	trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code, +			     dialog_token, status_code, buf, len); +	ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, +				   dialog_token, status_code, buf, len); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev, +				 struct net_device *dev, u8 *peer, +				 enum nl80211_tdls_operation oper) +{ +	int ret; +	trace_rdev_tdls_oper(&rdev->wiphy, dev, peer, oper); +	ret = rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, oper); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int rdev_probe_client(struct cfg80211_registered_device *rdev, +				    struct net_device *dev, const u8 *peer, +				    u64 *cookie) +{ +	int ret; +	trace_rdev_probe_client(&rdev->wiphy, dev, peer); +	ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie); +	trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); +	return ret; +} + +static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev, +				     struct net_device *dev, u16 noack_map) +{ +	int ret; +	trace_rdev_set_noack_map(&rdev->wiphy, dev, noack_map); +	ret = rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline int +rdev_get_et_sset_count(struct cfg80211_registered_device *rdev, +		       struct net_device *dev, int sset) +{ +	int ret; +	trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset); +	ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev, +				     struct net_device *dev, +				     struct ethtool_stats *stats, u64 *data) +{ +	trace_rdev_get_et_stats(&rdev->wiphy, dev); +	rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data); +	trace_rdev_return_void(&rdev->wiphy); +} + +static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev, +				       struct net_device *dev, u32 sset, +				       u8 *data) +{ +	trace_rdev_get_et_strings(&rdev->wiphy, dev, sset); +	rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data); +	trace_rdev_return_void(&rdev->wiphy); +} + +static inline int +rdev_get_channel(struct cfg80211_registered_device *rdev, +		 struct wireless_dev *wdev, +		 struct cfg80211_chan_def *chandef) +{ +	int ret; + +	trace_rdev_get_channel(&rdev->wiphy, wdev); +	ret = rdev->ops->get_channel(&rdev->wiphy, wdev, chandef); +	trace_rdev_return_chandef(&rdev->wiphy, ret, chandef); + +	return ret; +} + +static inline int rdev_start_p2p_device(struct cfg80211_registered_device *rdev, +					struct wireless_dev *wdev) +{ +	int ret; + +	trace_rdev_start_p2p_device(&rdev->wiphy, wdev); +	ret = rdev->ops->start_p2p_device(&rdev->wiphy, wdev); +	trace_rdev_return_int(&rdev->wiphy, ret); +	return ret; +} + +static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev, +					struct wireless_dev *wdev) +{ +	trace_rdev_stop_p2p_device(&rdev->wiphy, wdev); +	rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); +	trace_rdev_return_void(&rdev->wiphy); +}					 + +static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, +				   struct net_device *dev, +				   struct cfg80211_acl_data *params) +{ +	int ret; + +	trace_rdev_set_mac_acl(&rdev->wiphy, dev, params); +	ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params); +	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 bcc7d7ee5a5..98532c00242 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -48,7 +48,6 @@  #include <linux/export.h>  #include <linux/slab.h>  #include <linux/list.h> -#include <linux/random.h>  #include <linux/ctype.h>  #include <linux/nl80211.h>  #include <linux/platform_device.h> @@ -66,6 +65,13 @@  #define REG_DBG_PRINT(args...)  #endif +enum reg_request_treatment { +	REG_REQ_OK, +	REG_REQ_IGNORE, +	REG_REQ_INTERSECT, +	REG_REQ_ALREADY_SET, +}; +  static struct regulatory_request core_request_world = {  	.initiator = NL80211_REGDOM_SET_BY_CORE,  	.alpha2[0] = '0', @@ -76,7 +82,8 @@ static struct regulatory_request core_request_world = {  };  /* Receipt of information from last regulatory request */ -static struct regulatory_request *last_request = &core_request_world; +static struct regulatory_request __rcu *last_request = +	(void __rcu *)&core_request_world;  /* To trigger userspace events */  static struct platform_device *reg_pdev; @@ -88,16 +95,16 @@ static struct device_type reg_device_type = {  /*   * Central wireless core regulatory domains, we only need two,   * the current one and a world regulatory domain in case we have no - * information to give us an alpha2 + * information to give us an alpha2.   */ -const struct ieee80211_regdomain *cfg80211_regdomain; +const struct ieee80211_regdomain __rcu *cfg80211_regdomain;  /*   * Protects static reg.c components: - *     - cfg80211_world_regdom - *     - cfg80211_regdom - *     - last_request - *     - reg_num_devs_support_basehint + *	- cfg80211_regdomain (if not used with RCU) + *	- cfg80211_world_regdom + *	- last_request (if not used with RCU) + *	- reg_num_devs_support_basehint   */  static DEFINE_MUTEX(reg_mutex); @@ -112,6 +119,31 @@ static inline void assert_reg_lock(void)  	lockdep_assert_held(®_mutex);  } +static const struct ieee80211_regdomain *get_cfg80211_regdom(void) +{ +	return rcu_dereference_protected(cfg80211_regdomain, +					 lockdep_is_held(®_mutex)); +} + +static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) +{ +	return rcu_dereference_protected(wiphy->regd, +					 lockdep_is_held(®_mutex)); +} + +static void rcu_free_regdom(const struct ieee80211_regdomain *r) +{ +	if (!r) +		return; +	kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); +} + +static struct regulatory_request *get_last_request(void) +{ +	return rcu_dereference_check(last_request, +				     lockdep_is_held(®_mutex)); +} +  /* Used to queue up regulatory hints */  static LIST_HEAD(reg_requests_list);  static spinlock_t reg_requests_lock; @@ -141,9 +173,8 @@ static const struct ieee80211_regdomain world_regdom = {  	.reg_rules = {  		/* IEEE 802.11b/g, channels 1..11 */  		REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), -		/* IEEE 802.11b/g, channels 12..13. No HT40 -		 * channel fits here. */ -		REG_RULE(2467-10, 2472+10, 20, 6, 20, +		/* IEEE 802.11b/g, channels 12..13. */ +		REG_RULE(2467-10, 2472+10, 40, 6, 20,  			NL80211_RRF_PASSIVE_SCAN |  			NL80211_RRF_NO_IBSS),  		/* IEEE 802.11 channel 14 - Only JP enables @@ -178,28 +209,37 @@ static char user_alpha2[2];  module_param(ieee80211_regdom, charp, 0444);  MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); -static void reset_regdomains(bool full_reset) +static void reset_regdomains(bool full_reset, +			     const struct ieee80211_regdomain *new_regdom)  { +	const struct ieee80211_regdomain *r; +	struct regulatory_request *lr; + +	assert_reg_lock(); + +	r = get_cfg80211_regdom(); +  	/* avoid freeing static information or freeing something twice */ -	if (cfg80211_regdomain == cfg80211_world_regdom) -		cfg80211_regdomain = NULL; +	if (r == cfg80211_world_regdom) +		r = NULL;  	if (cfg80211_world_regdom == &world_regdom)  		cfg80211_world_regdom = NULL; -	if (cfg80211_regdomain == &world_regdom) -		cfg80211_regdomain = NULL; +	if (r == &world_regdom) +		r = NULL; -	kfree(cfg80211_regdomain); -	kfree(cfg80211_world_regdom); +	rcu_free_regdom(r); +	rcu_free_regdom(cfg80211_world_regdom);  	cfg80211_world_regdom = &world_regdom; -	cfg80211_regdomain = NULL; +	rcu_assign_pointer(cfg80211_regdomain, new_regdom);  	if (!full_reset)  		return; -	if (last_request != &core_request_world) -		kfree(last_request); -	last_request = &core_request_world; +	lr = get_last_request(); +	if (lr != &core_request_world && lr) +		kfree_rcu(lr, rcu_head); +	rcu_assign_pointer(last_request, &core_request_world);  }  /* @@ -208,30 +248,29 @@ static void reset_regdomains(bool full_reset)   */  static void update_world_regdomain(const struct ieee80211_regdomain *rd)  { -	BUG_ON(!last_request); +	struct regulatory_request *lr; -	reset_regdomains(false); +	lr = get_last_request(); + +	WARN_ON(!lr); + +	reset_regdomains(false, rd);  	cfg80211_world_regdom = rd; -	cfg80211_regdomain = rd;  }  bool is_world_regdom(const char *alpha2)  {  	if (!alpha2)  		return false; -	if (alpha2[0] == '0' && alpha2[1] == '0') -		return true; -	return false; +	return alpha2[0] == '0' && alpha2[1] == '0';  }  static bool is_alpha2_set(const char *alpha2)  {  	if (!alpha2)  		return false; -	if (alpha2[0] != 0 && alpha2[1] != 0) -		return true; -	return false; +	return alpha2[0] && alpha2[1];  }  static bool is_unknown_alpha2(const char *alpha2) @@ -242,9 +281,7 @@ static bool is_unknown_alpha2(const char *alpha2)  	 * Special case where regulatory domain was built by driver  	 * but a specific alpha2 cannot be determined  	 */ -	if (alpha2[0] == '9' && alpha2[1] == '9') -		return true; -	return false; +	return alpha2[0] == '9' && alpha2[1] == '9';  }  static bool is_intersected_alpha2(const char *alpha2) @@ -256,39 +293,30 @@ static bool is_intersected_alpha2(const char *alpha2)  	 * result of an intersection between two regulatory domain  	 * structures  	 */ -	if (alpha2[0] == '9' && alpha2[1] == '8') -		return true; -	return false; +	return alpha2[0] == '9' && alpha2[1] == '8';  }  static bool is_an_alpha2(const char *alpha2)  {  	if (!alpha2)  		return false; -	if (isalpha(alpha2[0]) && isalpha(alpha2[1])) -		return true; -	return false; +	return isalpha(alpha2[0]) && isalpha(alpha2[1]);  }  static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y)  {  	if (!alpha2_x || !alpha2_y)  		return false; -	if (alpha2_x[0] == alpha2_y[0] && -		alpha2_x[1] == alpha2_y[1]) -		return true; -	return false; +	return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1];  }  static bool regdom_changes(const char *alpha2)  { -	assert_cfg80211_lock(); +	const struct ieee80211_regdomain *r = get_cfg80211_regdom(); -	if (!cfg80211_regdomain) +	if (!r)  		return true; -	if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) -		return false; -	return true; +	return !alpha2_equal(r->alpha2, alpha2);  }  /* @@ -302,38 +330,36 @@ static bool is_user_regdom_saved(void)  		return false;  	/* This would indicate a mistake on the design */ -	if (WARN((!is_world_regdom(user_alpha2) && -		  !is_an_alpha2(user_alpha2)), +	if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2),  		 "Unexpected user alpha2: %c%c\n", -		 user_alpha2[0], -	         user_alpha2[1])) +		 user_alpha2[0], user_alpha2[1]))  		return false;  	return true;  } -static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, -			 const struct ieee80211_regdomain *src_regd) +static const struct ieee80211_regdomain * +reg_copy_regd(const struct ieee80211_regdomain *src_regd)  {  	struct ieee80211_regdomain *regd; -	int size_of_regd = 0; +	int size_of_regd;  	unsigned int i; -	size_of_regd = sizeof(struct ieee80211_regdomain) + -	  ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); +	size_of_regd = +		sizeof(struct ieee80211_regdomain) + +		src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);  	regd = kzalloc(size_of_regd, GFP_KERNEL);  	if (!regd) -		return -ENOMEM; +		return ERR_PTR(-ENOMEM);  	memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));  	for (i = 0; i < src_regd->n_reg_rules; i++)  		memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], -			sizeof(struct ieee80211_reg_rule)); +		       sizeof(struct ieee80211_reg_rule)); -	*dst_regd = regd; -	return 0; +	return regd;  }  #ifdef CONFIG_CFG80211_INTERNAL_REGDB @@ -348,9 +374,8 @@ static DEFINE_MUTEX(reg_regdb_search_mutex);  static void reg_regdb_search(struct work_struct *work)  {  	struct reg_regdb_search_request *request; -	const struct ieee80211_regdomain *curdom, *regdom; -	int i, r; -	bool set_reg = false; +	const struct ieee80211_regdomain *curdom, *regdom = NULL; +	int i;  	mutex_lock(&cfg80211_mutex); @@ -361,14 +386,11 @@ static void reg_regdb_search(struct work_struct *work)  					   list);  		list_del(&request->list); -		for (i=0; i<reg_regdb_size; i++) { +		for (i = 0; i < reg_regdb_size; i++) {  			curdom = reg_regdb[i]; -			if (!memcmp(request->alpha2, curdom->alpha2, 2)) { -				r = reg_copy_regd(®dom, curdom); -				if (r) -					break; -				set_reg = true; +			if (alpha2_equal(request->alpha2, curdom->alpha2)) { +				regdom = reg_copy_regd(curdom);  				break;  			}  		} @@ -377,7 +399,7 @@ static void reg_regdb_search(struct work_struct *work)  	}  	mutex_unlock(®_regdb_search_mutex); -	if (set_reg) +	if (!IS_ERR_OR_NULL(regdom))  		set_regdom(regdom);  	mutex_unlock(&cfg80211_mutex); @@ -435,15 +457,14 @@ static int call_crda(const char *alpha2)  	return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE);  } -/* Used by nl80211 before kmalloc'ing our regulatory domain */ -bool reg_is_valid_request(const char *alpha2) +static bool reg_is_valid_request(const char *alpha2)  { -	assert_cfg80211_lock(); +	struct regulatory_request *lr = get_last_request(); -	if (!last_request) +	if (!lr || lr->processed)  		return false; -	return alpha2_equal(last_request->alpha2, alpha2); +	return alpha2_equal(lr->alpha2, alpha2);  }  /* Sanity check on a regulatory rule */ @@ -461,7 +482,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)  	freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;  	if (freq_range->end_freq_khz <= freq_range->start_freq_khz || -			freq_range->max_bandwidth_khz > freq_diff) +	    freq_range->max_bandwidth_khz > freq_diff)  		return false;  	return true; @@ -488,8 +509,7 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)  }  static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, -			    u32 center_freq_khz, -			    u32 bw_khz) +			    u32 center_freq_khz, u32 bw_khz)  {  	u32 start_freq_khz, end_freq_khz; @@ -519,7 +539,7 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,   * regulatory rule support for other "bands".   **/  static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, -	u32 freq_khz) +			      u32 freq_khz)  {  #define ONE_GHZ_IN_KHZ	1000000  	/* @@ -541,10 +561,9 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,   * Helper for regdom_intersect(), this does the real   * mathematical intersection fun   */ -static int reg_rules_intersect( -	const struct ieee80211_reg_rule *rule1, -	const struct ieee80211_reg_rule *rule2, -	struct ieee80211_reg_rule *intersected_rule) +static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, +			       const struct ieee80211_reg_rule *rule2, +			       struct ieee80211_reg_rule *intersected_rule)  {  	const struct ieee80211_freq_range *freq_range1, *freq_range2;  	struct ieee80211_freq_range *freq_range; @@ -561,11 +580,11 @@ static int reg_rules_intersect(  	power_rule = &intersected_rule->power_rule;  	freq_range->start_freq_khz = max(freq_range1->start_freq_khz, -		freq_range2->start_freq_khz); +					 freq_range2->start_freq_khz);  	freq_range->end_freq_khz = min(freq_range1->end_freq_khz, -		freq_range2->end_freq_khz); +				       freq_range2->end_freq_khz);  	freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, -		freq_range2->max_bandwidth_khz); +					    freq_range2->max_bandwidth_khz);  	freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;  	if (freq_range->max_bandwidth_khz > freq_diff) @@ -576,7 +595,7 @@ static int reg_rules_intersect(  	power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,  		power_rule2->max_antenna_gain); -	intersected_rule->flags = (rule1->flags | rule2->flags); +	intersected_rule->flags = rule1->flags | rule2->flags;  	if (!is_valid_reg_rule(intersected_rule))  		return -EINVAL; @@ -597,9 +616,9 @@ static int reg_rules_intersect(   * resulting intersection of rules between rd1 and rd2. We will   * kzalloc() this structure for you.   */ -static struct ieee80211_regdomain *regdom_intersect( -	const struct ieee80211_regdomain *rd1, -	const struct ieee80211_regdomain *rd2) +static struct ieee80211_regdomain * +regdom_intersect(const struct ieee80211_regdomain *rd1, +		 const struct ieee80211_regdomain *rd2)  {  	int r, size_of_regd;  	unsigned int x, y; @@ -608,12 +627,7 @@ static struct ieee80211_regdomain *regdom_intersect(  	struct ieee80211_reg_rule *intersected_rule;  	struct ieee80211_regdomain *rd;  	/* This is just a dummy holder to help us count */ -	struct ieee80211_reg_rule irule; - -	/* Uses the stack temporarily for counter arithmetic */ -	intersected_rule = &irule; - -	memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); +	struct ieee80211_reg_rule dummy_rule;  	if (!rd1 || !rd2)  		return NULL; @@ -630,11 +644,8 @@ static struct ieee80211_regdomain *regdom_intersect(  		rule1 = &rd1->reg_rules[x];  		for (y = 0; y < rd2->n_reg_rules; y++) {  			rule2 = &rd2->reg_rules[y]; -			if (!reg_rules_intersect(rule1, rule2, -					intersected_rule)) +			if (!reg_rules_intersect(rule1, rule2, &dummy_rule))  				num_rules++; -			memset(intersected_rule, 0, -					sizeof(struct ieee80211_reg_rule));  		}  	} @@ -642,15 +653,15 @@ static struct ieee80211_regdomain *regdom_intersect(  		return NULL;  	size_of_regd = sizeof(struct ieee80211_regdomain) + -		((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); +		       num_rules * sizeof(struct ieee80211_reg_rule);  	rd = kzalloc(size_of_regd, GFP_KERNEL);  	if (!rd)  		return NULL; -	for (x = 0; x < rd1->n_reg_rules; x++) { +	for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) {  		rule1 = &rd1->reg_rules[x]; -		for (y = 0; y < rd2->n_reg_rules; y++) { +		for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) {  			rule2 = &rd2->reg_rules[y];  			/*  			 * This time around instead of using the stack lets @@ -658,8 +669,7 @@ static struct ieee80211_regdomain *regdom_intersect(  			 * a memcpy()  			 */  			intersected_rule = &rd->reg_rules[rule_idx]; -			r = reg_rules_intersect(rule1, rule2, -				intersected_rule); +			r = reg_rules_intersect(rule1, rule2, intersected_rule);  			/*  			 * No need to memset here the intersected rule here as  			 * we're not using the stack anymore @@ -700,34 +710,16 @@ static u32 map_regdom_flags(u32 rd_flags)  	return channel_flags;  } -static int freq_reg_info_regd(struct wiphy *wiphy, -			      u32 center_freq, -			      u32 desired_bw_khz, -			      const struct ieee80211_reg_rule **reg_rule, -			      const struct ieee80211_regdomain *custom_regd) +static const struct ieee80211_reg_rule * +freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, +		   const struct ieee80211_regdomain *regd)  {  	int i;  	bool band_rule_found = false; -	const struct ieee80211_regdomain *regd;  	bool bw_fits = false; -	if (!desired_bw_khz) -		desired_bw_khz = MHZ_TO_KHZ(20); - -	regd = custom_regd ? custom_regd : cfg80211_regdomain; - -	/* -	 * Follow the driver's regulatory domain, if present, unless a country -	 * IE has been processed or a user wants to help complaince further -	 */ -	if (!custom_regd && -	    last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && -	    last_request->initiator != NL80211_REGDOM_SET_BY_USER && -	    wiphy->regd) -		regd = wiphy->regd; -  	if (!regd) -		return -EINVAL; +		return ERR_PTR(-EINVAL);  	for (i = 0; i < regd->n_reg_rules; i++) {  		const struct ieee80211_reg_rule *rr; @@ -744,33 +736,36 @@ static int freq_reg_info_regd(struct wiphy *wiphy,  		if (!band_rule_found)  			band_rule_found = freq_in_rule_band(fr, center_freq); -		bw_fits = reg_does_bw_fit(fr, -					  center_freq, -					  desired_bw_khz); +		bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); -		if (band_rule_found && bw_fits) { -			*reg_rule = rr; -			return 0; -		} +		if (band_rule_found && bw_fits) +			return rr;  	}  	if (!band_rule_found) -		return -ERANGE; +		return ERR_PTR(-ERANGE); -	return -EINVAL; +	return ERR_PTR(-EINVAL);  } -int freq_reg_info(struct wiphy *wiphy, -		  u32 center_freq, -		  u32 desired_bw_khz, -		  const struct ieee80211_reg_rule **reg_rule) +const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, +					       u32 center_freq)  { -	assert_cfg80211_lock(); -	return freq_reg_info_regd(wiphy, -				  center_freq, -				  desired_bw_khz, -				  reg_rule, -				  NULL); +	const struct ieee80211_regdomain *regd; +	struct regulatory_request *lr = get_last_request(); + +	/* +	 * Follow the driver's regulatory domain, if present, unless a country +	 * IE has been processed or a user wants to help complaince further +	 */ +	if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && +	    lr->initiator != NL80211_REGDOM_SET_BY_USER && +	    wiphy->regd) +		regd = get_wiphy_regdom(wiphy); +	else +		regd = get_cfg80211_regdom(); + +	return freq_reg_info_regd(wiphy, center_freq, regd);  }  EXPORT_SYMBOL(freq_reg_info); @@ -793,7 +788,6 @@ static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)  }  static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, -				    u32 desired_bw_khz,  				    const struct ieee80211_reg_rule *reg_rule)  {  	const struct ieee80211_power_rule *power_rule; @@ -808,21 +802,16 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,  	else  		snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); -	REG_DBG_PRINT("Updating information on frequency %d MHz " -		      "for a %d MHz width channel with regulatory rule:\n", -		      chan->center_freq, -		      KHZ_TO_MHZ(desired_bw_khz)); +	REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n", +		      chan->center_freq);  	REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", -		      freq_range->start_freq_khz, -		      freq_range->end_freq_khz, -		      freq_range->max_bandwidth_khz, -		      max_antenna_gain, +		      freq_range->start_freq_khz, freq_range->end_freq_khz, +		      freq_range->max_bandwidth_khz, max_antenna_gain,  		      power_rule->max_eirp);  }  #else  static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, -				    u32 desired_bw_khz,  				    const struct ieee80211_reg_rule *reg_rule)  {  	return; @@ -832,43 +821,25 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,  /*   * Note that right now we assume the desired channel bandwidth   * is always 20 MHz for each individual channel (HT40 uses 20 MHz - * per channel, the primary and the extension channel). To support - * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a - * new ieee80211_channel.target_bw and re run the regulatory check - * on the wiphy with the target_bw specified. Then we can simply use - * that below for the desired_bw_khz below. + * per channel, the primary and the extension channel).   */  static void handle_channel(struct wiphy *wiphy,  			   enum nl80211_reg_initiator initiator, -			   enum ieee80211_band band, -			   unsigned int chan_idx) +			   struct ieee80211_channel *chan)  { -	int r;  	u32 flags, bw_flags = 0; -	u32 desired_bw_khz = MHZ_TO_KHZ(20);  	const struct ieee80211_reg_rule *reg_rule = NULL;  	const struct ieee80211_power_rule *power_rule = NULL;  	const struct ieee80211_freq_range *freq_range = NULL; -	struct ieee80211_supported_band *sband; -	struct ieee80211_channel *chan;  	struct wiphy *request_wiphy = NULL; +	struct regulatory_request *lr = get_last_request(); -	assert_cfg80211_lock(); - -	request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - -	sband = wiphy->bands[band]; -	BUG_ON(chan_idx >= sband->n_channels); -	chan = &sband->channels[chan_idx]; +	request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);  	flags = chan->orig_flags; -	r = freq_reg_info(wiphy, -			  MHZ_TO_KHZ(chan->center_freq), -			  desired_bw_khz, -			  ®_rule); - -	if (r) { +	reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); +	if (IS_ERR(reg_rule)) {  		/*  		 * We will disable all channels that do not match our  		 * received regulatory rule unless the hint is coming @@ -880,7 +851,7 @@ static void handle_channel(struct wiphy *wiphy,  		 * while 5 GHz is still supported.  		 */  		if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && -		    r == -ERANGE) +		    PTR_ERR(reg_rule) == -ERANGE)  			return;  		REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); @@ -888,15 +859,19 @@ static void handle_channel(struct wiphy *wiphy,  		return;  	} -	chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); +	chan_reg_rule_print_dbg(chan, reg_rule);  	power_rule = ®_rule->power_rule;  	freq_range = ®_rule->freq_range;  	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))  		bw_flags = IEEE80211_CHAN_NO_HT40; +	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) +		bw_flags |= IEEE80211_CHAN_NO_80MHZ; +	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) +		bw_flags |= IEEE80211_CHAN_NO_160MHZ; -	if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && +	if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&  	    request_wiphy && request_wiphy == wiphy &&  	    request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {  		/* @@ -913,10 +888,14 @@ static void handle_channel(struct wiphy *wiphy,  		return;  	} +	chan->dfs_state = NL80211_DFS_USABLE; +	chan->dfs_state_entered = jiffies; +  	chan->beacon_found = false;  	chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); -	chan->max_antenna_gain = min(chan->orig_mag, -		(int) MBI_TO_DBI(power_rule->max_antenna_gain)); +	chan->max_antenna_gain = +		min_t(int, chan->orig_mag, +		      MBI_TO_DBI(power_rule->max_antenna_gain));  	chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);  	if (chan->orig_mpwr) {  		/* @@ -936,68 +915,65 @@ static void handle_channel(struct wiphy *wiphy,  }  static void handle_band(struct wiphy *wiphy, -			enum ieee80211_band band, -			enum nl80211_reg_initiator initiator) +			enum nl80211_reg_initiator initiator, +			struct ieee80211_supported_band *sband)  {  	unsigned int i; -	struct ieee80211_supported_band *sband; -	BUG_ON(!wiphy->bands[band]); -	sband = wiphy->bands[band]; +	if (!sband) +		return;  	for (i = 0; i < sband->n_channels; i++) -		handle_channel(wiphy, initiator, band, i); +		handle_channel(wiphy, initiator, &sband->channels[i]);  }  static bool reg_request_cell_base(struct regulatory_request *request)  {  	if (request->initiator != NL80211_REGDOM_SET_BY_USER)  		return false; -	if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) -		return false; -	return true; +	return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;  }  bool reg_last_request_cell_base(void)  {  	bool val; -	assert_cfg80211_lock();  	mutex_lock(®_mutex); -	val = reg_request_cell_base(last_request); +	val = reg_request_cell_base(get_last_request());  	mutex_unlock(®_mutex); +  	return val;  }  #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS -  /* Core specific check */ -static int reg_ignore_cell_hint(struct regulatory_request *pending_request) +static enum reg_request_treatment +reg_ignore_cell_hint(struct regulatory_request *pending_request)  { +	struct regulatory_request *lr = get_last_request(); +  	if (!reg_num_devs_support_basehint) -		return -EOPNOTSUPP; +		return REG_REQ_IGNORE; -	if (reg_request_cell_base(last_request)) { -		if (!regdom_changes(pending_request->alpha2)) -			return -EALREADY; -		return 0; -	} -	return 0; +	if (reg_request_cell_base(lr) && +	    !regdom_changes(pending_request->alpha2)) +		return REG_REQ_ALREADY_SET; + +	return REG_REQ_OK;  }  /* Device specific check */  static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)  { -	if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) -		return true; -	return false; +	return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS);  }  #else  static int reg_ignore_cell_hint(struct regulatory_request *pending_request)  { -	return -EOPNOTSUPP; +	return REG_REQ_IGNORE;  } -static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) + +static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)  {  	return true;  } @@ -1007,18 +983,17 @@ static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)  static bool ignore_reg_update(struct wiphy *wiphy,  			      enum nl80211_reg_initiator initiator)  { -	if (!last_request) { -		REG_DBG_PRINT("Ignoring regulatory request %s since " -			      "last_request is not set\n", +	struct regulatory_request *lr = get_last_request(); + +	if (!lr) { +		REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n",  			      reg_initiator_name(initiator));  		return true;  	}  	if (initiator == NL80211_REGDOM_SET_BY_CORE &&  	    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { -		REG_DBG_PRINT("Ignoring regulatory request %s " -			      "since the driver uses its own custom " -			      "regulatory domain\n", +		REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n",  			      reg_initiator_name(initiator));  		return true;  	} @@ -1029,22 +1004,35 @@ static bool ignore_reg_update(struct wiphy *wiphy,  	 */  	if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&  	    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && -	    !is_world_regdom(last_request->alpha2)) { -		REG_DBG_PRINT("Ignoring regulatory request %s " -			      "since the driver requires its own regulatory " -			      "domain to be set first\n", +	    !is_world_regdom(lr->alpha2)) { +		REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n",  			      reg_initiator_name(initiator));  		return true;  	} -	if (reg_request_cell_base(last_request)) +	if (reg_request_cell_base(lr))  		return reg_dev_ignore_cell_hint(wiphy);  	return false;  } -static void handle_reg_beacon(struct wiphy *wiphy, -			      unsigned int chan_idx, +static bool reg_is_world_roaming(struct wiphy *wiphy) +{ +	const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); +	const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); +	struct regulatory_request *lr = get_last_request(); + +	if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) +		return true; + +	if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && +	    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) +		return true; + +	return false; +} + +static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,  			      struct reg_beacon *reg_beacon)  {  	struct ieee80211_supported_band *sband; @@ -1052,8 +1040,6 @@ static void handle_reg_beacon(struct wiphy *wiphy,  	bool channel_changed = false;  	struct ieee80211_channel chan_before; -	assert_cfg80211_lock(); -  	sband = wiphy->bands[reg_beacon->chan.band];  	chan = &sband->channels[chan_idx]; @@ -1065,6 +1051,9 @@ static void handle_reg_beacon(struct wiphy *wiphy,  	chan->beacon_found = true; +	if (!reg_is_world_roaming(wiphy)) +		return; +  	if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS)  		return; @@ -1095,8 +1084,6 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy,  	unsigned int i;  	struct ieee80211_supported_band *sband; -	assert_cfg80211_lock(); -  	if (!wiphy->bands[reg_beacon->chan.band])  		return; @@ -1115,11 +1102,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy)  	struct ieee80211_supported_band *sband;  	struct reg_beacon *reg_beacon; -	assert_cfg80211_lock(); - -	if (list_empty(®_beacon_list)) -		return; -  	list_for_each_entry(reg_beacon, ®_beacon_list, list) {  		if (!wiphy->bands[reg_beacon->chan.band])  			continue; @@ -1129,18 +1111,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy)  	}  } -static bool reg_is_world_roaming(struct wiphy *wiphy) -{ -	if (is_world_regdom(cfg80211_regdomain->alpha2) || -	    (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) -		return true; -	if (last_request && -	    last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && -	    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) -		return true; -	return false; -} -  /* Reap the advantages of previously found beacons */  static void reg_process_beacons(struct wiphy *wiphy)  { @@ -1150,39 +1120,29 @@ static void reg_process_beacons(struct wiphy *wiphy)  	 */  	if (!last_request)  		return; -	if (!reg_is_world_roaming(wiphy)) -		return;  	wiphy_update_beacon_reg(wiphy);  } -static bool is_ht40_not_allowed(struct ieee80211_channel *chan) +static bool is_ht40_allowed(struct ieee80211_channel *chan)  {  	if (!chan) -		return true; +		return false;  	if (chan->flags & IEEE80211_CHAN_DISABLED) -		return true; +		return false;  	/* This would happen when regulatory rules disallow HT40 completely */ -	if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) -		return true; -	return false; +	if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40) +		return false; +	return true;  }  static void reg_process_ht_flags_channel(struct wiphy *wiphy, -					 enum ieee80211_band band, -					 unsigned int chan_idx) +					 struct ieee80211_channel *channel)  { -	struct ieee80211_supported_band *sband; -	struct ieee80211_channel *channel; +	struct ieee80211_supported_band *sband = wiphy->bands[channel->band];  	struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;  	unsigned int i; -	assert_cfg80211_lock(); - -	sband = wiphy->bands[band]; -	BUG_ON(chan_idx >= sband->n_channels); -	channel = &sband->channels[chan_idx]; - -	if (is_ht40_not_allowed(channel)) { +	if (!is_ht40_allowed(channel)) {  		channel->flags |= IEEE80211_CHAN_NO_HT40;  		return;  	} @@ -1193,6 +1153,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy,  	 */  	for (i = 0; i < sband->n_channels; i++) {  		struct ieee80211_channel *c = &sband->channels[i]; +  		if (c->center_freq == (channel->center_freq - 20))  			channel_before = c;  		if (c->center_freq == (channel->center_freq + 20)) @@ -1204,28 +1165,27 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy,  	 * if that ever changes we also need to change the below logic  	 * to include that as well.  	 */ -	if (is_ht40_not_allowed(channel_before)) +	if (!is_ht40_allowed(channel_before))  		channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;  	else  		channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; -	if (is_ht40_not_allowed(channel_after)) +	if (!is_ht40_allowed(channel_after))  		channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;  	else  		channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;  }  static void reg_process_ht_flags_band(struct wiphy *wiphy, -				      enum ieee80211_band band) +				      struct ieee80211_supported_band *sband)  {  	unsigned int i; -	struct ieee80211_supported_band *sband; -	BUG_ON(!wiphy->bands[band]); -	sband = wiphy->bands[band]; +	if (!sband) +		return;  	for (i = 0; i < sband->n_channels; i++) -		reg_process_ht_flags_channel(wiphy, band, i); +		reg_process_ht_flags_channel(wiphy, &sband->channels[i]);  }  static void reg_process_ht_flags(struct wiphy *wiphy) @@ -1235,34 +1195,29 @@ static void reg_process_ht_flags(struct wiphy *wiphy)  	if (!wiphy)  		return; -	for (band = 0; band < IEEE80211_NUM_BANDS; band++) { -		if (wiphy->bands[band]) -			reg_process_ht_flags_band(wiphy, band); -	} - +	for (band = 0; band < IEEE80211_NUM_BANDS; band++) +		reg_process_ht_flags_band(wiphy, wiphy->bands[band]);  }  static void wiphy_update_regulatory(struct wiphy *wiphy,  				    enum nl80211_reg_initiator initiator)  {  	enum ieee80211_band band; - -	assert_reg_lock(); +	struct regulatory_request *lr = get_last_request();  	if (ignore_reg_update(wiphy, initiator))  		return; -	last_request->dfs_region = cfg80211_regdomain->dfs_region; +	lr->dfs_region = get_cfg80211_regdom()->dfs_region; -	for (band = 0; band < IEEE80211_NUM_BANDS; band++) { -		if (wiphy->bands[band]) -			handle_band(wiphy, band, initiator); -	} +	for (band = 0; band < IEEE80211_NUM_BANDS; band++) +		handle_band(wiphy, initiator, wiphy->bands[band]);  	reg_process_beacons(wiphy);  	reg_process_ht_flags(wiphy); +  	if (wiphy->reg_notifier) -		wiphy->reg_notifier(wiphy, last_request); +		wiphy->reg_notifier(wiphy, lr);  }  static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) @@ -1270,6 +1225,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)  	struct cfg80211_registered_device *rdev;  	struct wiphy *wiphy; +	assert_cfg80211_lock(); +  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {  		wiphy = &rdev->wiphy;  		wiphy_update_regulatory(wiphy, initiator); @@ -1281,53 +1238,40 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)  		if (initiator == NL80211_REGDOM_SET_BY_CORE &&  		    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&  		    wiphy->reg_notifier) -			wiphy->reg_notifier(wiphy, last_request); +			wiphy->reg_notifier(wiphy, get_last_request());  	}  }  static void handle_channel_custom(struct wiphy *wiphy, -				  enum ieee80211_band band, -				  unsigned int chan_idx, +				  struct ieee80211_channel *chan,  				  const struct ieee80211_regdomain *regd)  { -	int r; -	u32 desired_bw_khz = MHZ_TO_KHZ(20);  	u32 bw_flags = 0;  	const struct ieee80211_reg_rule *reg_rule = NULL;  	const struct ieee80211_power_rule *power_rule = NULL;  	const struct ieee80211_freq_range *freq_range = NULL; -	struct ieee80211_supported_band *sband; -	struct ieee80211_channel *chan; -	assert_reg_lock(); +	reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), +				      regd); -	sband = wiphy->bands[band]; -	BUG_ON(chan_idx >= sband->n_channels); -	chan = &sband->channels[chan_idx]; - -	r = freq_reg_info_regd(wiphy, -			       MHZ_TO_KHZ(chan->center_freq), -			       desired_bw_khz, -			       ®_rule, -			       regd); - -	if (r) { -		REG_DBG_PRINT("Disabling freq %d MHz as custom " -			      "regd has no rule that fits a %d MHz " -			      "wide channel\n", -			      chan->center_freq, -			      KHZ_TO_MHZ(desired_bw_khz)); +	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;  		return;  	} -	chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); +	chan_reg_rule_print_dbg(chan, reg_rule);  	power_rule = ®_rule->power_rule;  	freq_range = ®_rule->freq_range;  	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))  		bw_flags = IEEE80211_CHAN_NO_HT40; +	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) +		bw_flags |= IEEE80211_CHAN_NO_80MHZ; +	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) +		bw_flags |= IEEE80211_CHAN_NO_160MHZ;  	chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;  	chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); @@ -1335,17 +1279,17 @@ static void handle_channel_custom(struct wiphy *wiphy,  		(int) MBM_TO_DBM(power_rule->max_eirp);  } -static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, +static void handle_band_custom(struct wiphy *wiphy, +			       struct ieee80211_supported_band *sband,  			       const struct ieee80211_regdomain *regd)  {  	unsigned int i; -	struct ieee80211_supported_band *sband; -	BUG_ON(!wiphy->bands[band]); -	sband = wiphy->bands[band]; +	if (!sband) +		return;  	for (i = 0; i < sband->n_channels; i++) -		handle_channel_custom(wiphy, band, i, regd); +		handle_channel_custom(wiphy, &sband->channels[i], regd);  }  /* Used by drivers prior to wiphy registration */ @@ -1355,60 +1299,50 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,  	enum ieee80211_band band;  	unsigned int bands_set = 0; -	mutex_lock(®_mutex);  	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {  		if (!wiphy->bands[band])  			continue; -		handle_band_custom(wiphy, band, regd); +		handle_band_custom(wiphy, wiphy->bands[band], regd);  		bands_set++;  	} -	mutex_unlock(®_mutex);  	/*  	 * no point in calling this if it won't have any effect -	 * on your device's supportd bands. +	 * on your device's supported bands.  	 */  	WARN_ON(!bands_set);  }  EXPORT_SYMBOL(wiphy_apply_custom_regulatory); -/* - * Return value which can be used by ignore_request() to indicate - * it has been determined we should intersect two regulatory domains - */ -#define REG_INTERSECT	1 -  /* This has the logic which determines when a new request   * should be ignored. */ -static int ignore_request(struct wiphy *wiphy, +static enum reg_request_treatment +get_reg_request_treatment(struct wiphy *wiphy,  			  struct regulatory_request *pending_request)  {  	struct wiphy *last_wiphy = NULL; - -	assert_cfg80211_lock(); +	struct regulatory_request *lr = get_last_request();  	/* All initial requests are respected */ -	if (!last_request) -		return 0; +	if (!lr) +		return REG_REQ_OK;  	switch (pending_request->initiator) {  	case NL80211_REGDOM_SET_BY_CORE: -		return 0; +		return REG_REQ_OK;  	case NL80211_REGDOM_SET_BY_COUNTRY_IE: - -		if (reg_request_cell_base(last_request)) { +		if (reg_request_cell_base(lr)) {  			/* Trust a Cell base station over the AP's country IE */  			if (regdom_changes(pending_request->alpha2)) -				return -EOPNOTSUPP; -			return -EALREADY; +				return REG_REQ_IGNORE; +			return REG_REQ_ALREADY_SET;  		} -		last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); +		last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);  		if (unlikely(!is_an_alpha2(pending_request->alpha2)))  			return -EINVAL; -		if (last_request->initiator == -		    NL80211_REGDOM_SET_BY_COUNTRY_IE) { +		if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {  			if (last_wiphy != wiphy) {  				/*  				 * Two cards with two APs claiming different @@ -1417,23 +1351,23 @@ static int ignore_request(struct wiphy *wiphy,  				 * to be correct. Reject second one for now.  				 */  				if (regdom_changes(pending_request->alpha2)) -					return -EOPNOTSUPP; -				return -EALREADY; +					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 0; -			return -EALREADY; +				return REG_REQ_OK; +			return REG_REQ_ALREADY_SET;  		}  		return 0;  	case NL80211_REGDOM_SET_BY_DRIVER: -		if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { +		if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {  			if (regdom_changes(pending_request->alpha2)) -				return 0; -			return -EALREADY; +				return REG_REQ_OK; +			return REG_REQ_ALREADY_SET;  		}  		/* @@ -1441,59 +1375,59 @@ static int ignore_request(struct wiphy *wiphy,  		 * back in or if you add a new device for which the previously  		 * loaded card also agrees on the regulatory domain.  		 */ -		if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && +		if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&  		    !regdom_changes(pending_request->alpha2)) -			return -EALREADY; +			return REG_REQ_ALREADY_SET; -		return REG_INTERSECT; +		return REG_REQ_INTERSECT;  	case NL80211_REGDOM_SET_BY_USER:  		if (reg_request_cell_base(pending_request))  			return reg_ignore_cell_hint(pending_request); -		if (reg_request_cell_base(last_request)) -			return -EOPNOTSUPP; +		if (reg_request_cell_base(lr)) +			return REG_REQ_IGNORE; -		if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) -			return REG_INTERSECT; +		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 (last_request->initiator == NL80211_REGDOM_SET_BY_USER && -			  last_request->intersect) -			return -EOPNOTSUPP; +		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 (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || -		    last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || -		    last_request->initiator == NL80211_REGDOM_SET_BY_USER) { -			if (regdom_changes(last_request->alpha2)) -				return -EAGAIN; -		} +		if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || +		     lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || +		     lr->initiator == NL80211_REGDOM_SET_BY_USER) && +		    regdom_changes(lr->alpha2)) +			return REG_REQ_IGNORE;  		if (!regdom_changes(pending_request->alpha2)) -			return -EALREADY; +			return REG_REQ_ALREADY_SET; -		return 0; +		return REG_REQ_OK;  	} -	return -EINVAL; +	return REG_REQ_IGNORE;  }  static void reg_set_request_processed(void)  {  	bool need_more_processing = false; +	struct regulatory_request *lr = get_last_request(); -	last_request->processed = true; +	lr->processed = true;  	spin_lock(®_requests_lock);  	if (!list_empty(®_requests_list))  		need_more_processing = true;  	spin_unlock(®_requests_lock); -	if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) +	if (lr->initiator == NL80211_REGDOM_SET_BY_USER)  		cancel_delayed_work(®_timeout);  	if (need_more_processing) @@ -1509,116 +1443,122 @@ static void reg_set_request_processed(void)   * The Wireless subsystem can use this function to hint to the wireless core   * what it believes should be the current regulatory domain.   * - * Returns zero if all went fine, %-EALREADY if a regulatory domain had - * already been set or other standard error codes. + * Returns one of the different reg request treatment values.   * - * Caller must hold &cfg80211_mutex and ®_mutex + * Caller must hold ®_mutex   */ -static int __regulatory_hint(struct wiphy *wiphy, -			     struct regulatory_request *pending_request) +static enum reg_request_treatment +__regulatory_hint(struct wiphy *wiphy, +		  struct regulatory_request *pending_request)  { +	const struct ieee80211_regdomain *regd;  	bool intersect = false; -	int r = 0; - -	assert_cfg80211_lock(); +	enum reg_request_treatment treatment; +	struct regulatory_request *lr; -	r = ignore_request(wiphy, pending_request); +	treatment = get_reg_request_treatment(wiphy, pending_request); -	if (r == REG_INTERSECT) { +	switch (treatment) { +	case REG_REQ_INTERSECT:  		if (pending_request->initiator ==  		    NL80211_REGDOM_SET_BY_DRIVER) { -			r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); -			if (r) { +			regd = reg_copy_regd(get_cfg80211_regdom()); +			if (IS_ERR(regd)) {  				kfree(pending_request); -				return r; +				return PTR_ERR(regd);  			} +			rcu_assign_pointer(wiphy->regd, regd);  		}  		intersect = true; -	} else if (r) { +		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 (r == -EALREADY && -		    pending_request->initiator == -		    NL80211_REGDOM_SET_BY_DRIVER) { -			r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); -			if (r) { +		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 r; +				return REG_REQ_IGNORE;  			} -			r = -EALREADY; +			treatment = REG_REQ_ALREADY_SET; +			rcu_assign_pointer(wiphy->regd, regd);  			goto new_request;  		}  		kfree(pending_request); -		return r; +		return treatment;  	}  new_request: -	if (last_request != &core_request_world) -		kfree(last_request); +	lr = get_last_request(); +	if (lr != &core_request_world && lr) +		kfree_rcu(lr, rcu_head); -	last_request = pending_request; -	last_request->intersect = intersect; +	pending_request->intersect = intersect; +	pending_request->processed = false; +	rcu_assign_pointer(last_request, pending_request); +	lr = pending_request;  	pending_request = NULL; -	if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { -		user_alpha2[0] = last_request->alpha2[0]; -		user_alpha2[1] = last_request->alpha2[1]; +	if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { +		user_alpha2[0] = lr->alpha2[0]; +		user_alpha2[1] = lr->alpha2[1];  	} -	/* When r == REG_INTERSECT we do need to call CRDA */ -	if (r < 0) { +	/* When r == REG_REQ_INTERSECT we do need to call CRDA */ +	if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) {  		/*  		 * Since CRDA will not be called in this case as we already  		 * have applied the requested regulatory domain before we just  		 * inform userspace we have processed the request  		 */ -		if (r == -EALREADY) { -			nl80211_send_reg_change_event(last_request); +		if (treatment == REG_REQ_ALREADY_SET) { +			nl80211_send_reg_change_event(lr);  			reg_set_request_processed();  		} -		return r; +		return treatment;  	} -	return call_crda(last_request->alpha2); +	if (call_crda(lr->alpha2)) +		return REG_REQ_IGNORE; +	return REG_REQ_OK;  }  /* This processes *all* regulatory hints */  static void reg_process_hint(struct regulatory_request *reg_request,  			     enum nl80211_reg_initiator reg_initiator)  { -	int r = 0;  	struct wiphy *wiphy = NULL; -	BUG_ON(!reg_request->alpha2); +	if (WARN_ON(!reg_request->alpha2)) +		return; -	if (wiphy_idx_valid(reg_request->wiphy_idx)) +	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_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {  		kfree(reg_request);  		return;  	} -	r = __regulatory_hint(wiphy, reg_request); -	/* This is required so that the orig_* parameters are saved */ -	if (r == -EALREADY && wiphy && -	    wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { -		wiphy_update_regulatory(wiphy, reg_initiator); -		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); +		break; +	default: +		if (reg_initiator == NL80211_REGDOM_SET_BY_USER) +			schedule_delayed_work(®_timeout, +					      msecs_to_jiffies(3142)); +		break;  	} - -	/* -	 * We only time out user hints, given that they should be the only -	 * source of bogus requests. -	 */ -	if (r != -EALREADY && -	    reg_initiator == NL80211_REGDOM_SET_BY_USER) -		schedule_delayed_work(®_timeout, msecs_to_jiffies(3142));  }  /* @@ -1628,15 +1568,15 @@ static void reg_process_hint(struct regulatory_request *reg_request,   */  static void reg_process_pending_hints(void)  { -	struct regulatory_request *reg_request; +	struct regulatory_request *reg_request, *lr;  	mutex_lock(&cfg80211_mutex);  	mutex_lock(®_mutex); +	lr = get_last_request();  	/* When last_request->processed becomes true this will be rescheduled */ -	if (last_request && !last_request->processed) { -		REG_DBG_PRINT("Pending regulatory request, waiting " -			      "for it to be processed...\n"); +	if (lr && !lr->processed) { +		REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n");  		goto out;  	} @@ -1667,23 +1607,14 @@ static void reg_process_pending_beacon_hints(void)  	struct cfg80211_registered_device *rdev;  	struct reg_beacon *pending_beacon, *tmp; -	/* -	 * No need to hold the reg_mutex here as we just touch wiphys -	 * and do not read or access regulatory variables. -	 */  	mutex_lock(&cfg80211_mutex); +	mutex_lock(®_mutex);  	/* This goes through the _pending_ beacon list */  	spin_lock_bh(®_pending_beacons_lock); -	if (list_empty(®_pending_beacons)) { -		spin_unlock_bh(®_pending_beacons_lock); -		goto out; -	} -  	list_for_each_entry_safe(pending_beacon, tmp,  				 ®_pending_beacons, list) { -  		list_del_init(&pending_beacon->list);  		/* Applies the beacon hint to current wiphys */ @@ -1695,7 +1626,7 @@ static void reg_process_pending_beacon_hints(void)  	}  	spin_unlock_bh(®_pending_beacons_lock); -out: +	mutex_unlock(®_mutex);  	mutex_unlock(&cfg80211_mutex);  } @@ -1707,10 +1638,8 @@ static void reg_todo(struct work_struct *work)  static void queue_regulatory_request(struct regulatory_request *request)  { -	if (isalpha(request->alpha2[0])) -		request->alpha2[0] = toupper(request->alpha2[0]); -	if (isalpha(request->alpha2[1])) -		request->alpha2[1] = toupper(request->alpha2[1]); +	request->alpha2[0] = toupper(request->alpha2[0]); +	request->alpha2[1] = toupper(request->alpha2[1]);  	spin_lock(®_requests_lock);  	list_add_tail(&request->list, ®_requests_list); @@ -1727,8 +1656,7 @@ static int regulatory_hint_core(const char *alpha2)  {  	struct regulatory_request *request; -	request = kzalloc(sizeof(struct regulatory_request), -			  GFP_KERNEL); +	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);  	if (!request)  		return -ENOMEM; @@ -1747,13 +1675,14 @@ int regulatory_hint_user(const char *alpha2,  {  	struct regulatory_request *request; -	BUG_ON(!alpha2); +	if (WARN_ON(!alpha2)) +		return -EINVAL;  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);  	if (!request)  		return -ENOMEM; -	request->wiphy_idx = WIPHY_IDX_STALE; +	request->wiphy_idx = WIPHY_IDX_INVALID;  	request->alpha2[0] = alpha2[0];  	request->alpha2[1] = alpha2[1];  	request->initiator = NL80211_REGDOM_SET_BY_USER; @@ -1769,8 +1698,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)  {  	struct regulatory_request *request; -	BUG_ON(!alpha2); -	BUG_ON(!wiphy); +	if (WARN_ON(!alpha2 || !wiphy)) +		return -EINVAL;  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);  	if (!request) @@ -1778,9 +1707,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)  	request->wiphy_idx = get_wiphy_idx(wiphy); -	/* Must have registered wiphy first */ -	BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); -  	request->alpha2[0] = alpha2[0];  	request->alpha2[1] = alpha2[1];  	request->initiator = NL80211_REGDOM_SET_BY_DRIVER; @@ -1795,18 +1721,17 @@ EXPORT_SYMBOL(regulatory_hint);   * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and   * therefore cannot iterate over the rdev list here.   */ -void regulatory_hint_11d(struct wiphy *wiphy, -			 enum ieee80211_band band, -			 u8 *country_ie, -			 u8 country_ie_len) +void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, +			 const u8 *country_ie, u8 country_ie_len)  {  	char alpha2[2];  	enum environment_cap env = ENVIRON_ANY; -	struct regulatory_request *request; +	struct regulatory_request *request, *lr;  	mutex_lock(®_mutex); +	lr = get_last_request(); -	if (unlikely(!last_request)) +	if (unlikely(!lr))  		goto out;  	/* IE len must be evenly divisible by 2 */ @@ -1829,9 +1754,8 @@ void regulatory_hint_11d(struct wiphy *wiphy,  	 * We leave conflict resolution to the workqueue, where can hold  	 * cfg80211_mutex.  	 */ -	if (likely(last_request->initiator == -	    NL80211_REGDOM_SET_BY_COUNTRY_IE && -	    wiphy_idx_valid(last_request->wiphy_idx))) +	if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && +	    lr->wiphy_idx != WIPHY_IDX_INVALID)  		goto out;  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -1844,12 +1768,7 @@ void regulatory_hint_11d(struct wiphy *wiphy,  	request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;  	request->country_ie_env = env; -	mutex_unlock(®_mutex); -  	queue_regulatory_request(request); - -	return; -  out:  	mutex_unlock(®_mutex);  } @@ -1864,8 +1783,7 @@ static void restore_alpha2(char *alpha2, bool reset_user)  	if (is_user_regdom_saved()) {  		/* Unless we're asked to ignore it and reset it */  		if (reset_user) { -			REG_DBG_PRINT("Restoring regulatory settings " -			       "including user preference\n"); +			REG_DBG_PRINT("Restoring regulatory settings including user preference\n");  			user_alpha2[0] = '9';  			user_alpha2[1] = '7'; @@ -1875,26 +1793,20 @@ static void restore_alpha2(char *alpha2, bool reset_user)  			 * back as they were for a full restore.  			 */  			if (!is_world_regdom(ieee80211_regdom)) { -				REG_DBG_PRINT("Keeping preference on " -				       "module parameter ieee80211_regdom: %c%c\n", -				       ieee80211_regdom[0], -				       ieee80211_regdom[1]); +				REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", +					      ieee80211_regdom[0], ieee80211_regdom[1]);  				alpha2[0] = ieee80211_regdom[0];  				alpha2[1] = ieee80211_regdom[1];  			}  		} else { -			REG_DBG_PRINT("Restoring regulatory settings " -			       "while preserving user preference for: %c%c\n", -			       user_alpha2[0], -			       user_alpha2[1]); +			REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n", +				      user_alpha2[0], user_alpha2[1]);  			alpha2[0] = user_alpha2[0];  			alpha2[1] = user_alpha2[1];  		}  	} else if (!is_world_regdom(ieee80211_regdom)) { -		REG_DBG_PRINT("Keeping preference on " -		       "module parameter ieee80211_regdom: %c%c\n", -		       ieee80211_regdom[0], -		       ieee80211_regdom[1]); +		REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", +			      ieee80211_regdom[0], ieee80211_regdom[1]);  		alpha2[0] = ieee80211_regdom[0];  		alpha2[1] = ieee80211_regdom[1];  	} else @@ -1949,7 +1861,7 @@ static void restore_regulatory_settings(bool reset_user)  	mutex_lock(&cfg80211_mutex);  	mutex_lock(®_mutex); -	reset_regdomains(true); +	reset_regdomains(true, &world_regdom);  	restore_alpha2(alpha2, reset_user);  	/* @@ -1959,49 +1871,35 @@ static void restore_regulatory_settings(bool reset_user)  	 * settings.  	 */  	spin_lock(®_requests_lock); -	if (!list_empty(®_requests_list)) { -		list_for_each_entry_safe(reg_request, tmp, -					 ®_requests_list, list) { -			if (reg_request->initiator != -			    NL80211_REGDOM_SET_BY_USER) -				continue; -			list_move_tail(®_request->list, &tmp_reg_req_list); -		} +	list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { +		if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) +			continue; +		list_move_tail(®_request->list, &tmp_reg_req_list);  	}  	spin_unlock(®_requests_lock);  	/* Clear beacon hints */  	spin_lock_bh(®_pending_beacons_lock); -	if (!list_empty(®_pending_beacons)) { -		list_for_each_entry_safe(reg_beacon, btmp, -					 ®_pending_beacons, list) { -			list_del(®_beacon->list); -			kfree(reg_beacon); -		} +	list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { +		list_del(®_beacon->list); +		kfree(reg_beacon);  	}  	spin_unlock_bh(®_pending_beacons_lock); -	if (!list_empty(®_beacon_list)) { -		list_for_each_entry_safe(reg_beacon, btmp, -					 ®_beacon_list, list) { -			list_del(®_beacon->list); -			kfree(reg_beacon); -		} +	list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { +		list_del(®_beacon->list); +		kfree(reg_beacon);  	}  	/* First restore to the basic regulatory settings */ -	cfg80211_regdomain = cfg80211_world_regdom; -	world_alpha2[0] = cfg80211_regdomain->alpha2[0]; -	world_alpha2[1] = cfg80211_regdomain->alpha2[1]; +	world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; +	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)  			restore_custom_reg_settings(&rdev->wiphy);  	} -	mutex_unlock(®_mutex); -	mutex_unlock(&cfg80211_mutex); -  	regulatory_hint_core(world_alpha2);  	/* @@ -2012,20 +1910,8 @@ static void restore_regulatory_settings(bool reset_user)  	if (is_an_alpha2(alpha2))  		regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); -	if (list_empty(&tmp_reg_req_list)) -		return; - -	mutex_lock(&cfg80211_mutex); -	mutex_lock(®_mutex); -  	spin_lock(®_requests_lock); -	list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { -		REG_DBG_PRINT("Adding request for country %c%c back " -			      "into the queue\n", -			      reg_request->alpha2[0], -			      reg_request->alpha2[1]); -		list_move_tail(®_request->list, ®_requests_list); -	} +	list_splice_tail_init(&tmp_reg_req_list, ®_requests_list);  	spin_unlock(®_requests_lock);  	mutex_unlock(®_mutex); @@ -2038,8 +1924,7 @@ static void restore_regulatory_settings(bool reset_user)  void regulatory_hint_disconnect(void)  { -	REG_DBG_PRINT("All devices are disconnected, going to " -		      "restore regulatory settings\n"); +	REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n");  	restore_regulatory_settings(false);  } @@ -2052,31 +1937,48 @@ static bool freq_is_chan_12_13_14(u16 freq)  	return false;  } +static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan) +{ +	struct reg_beacon *pending_beacon; + +	list_for_each_entry(pending_beacon, ®_pending_beacons, list) +		if (beacon_chan->center_freq == +		    pending_beacon->chan.center_freq) +			return true; +	return false; +} +  int regulatory_hint_found_beacon(struct wiphy *wiphy,  				 struct ieee80211_channel *beacon_chan,  				 gfp_t gfp)  {  	struct reg_beacon *reg_beacon; +	bool processing; -	if (likely((beacon_chan->beacon_found || -	    (beacon_chan->flags & IEEE80211_CHAN_RADAR) || +	if (beacon_chan->beacon_found || +	    beacon_chan->flags & IEEE80211_CHAN_RADAR ||  	    (beacon_chan->band == IEEE80211_BAND_2GHZ && -	     !freq_is_chan_12_13_14(beacon_chan->center_freq))))) +	     !freq_is_chan_12_13_14(beacon_chan->center_freq))) +		return 0; + +	spin_lock_bh(®_pending_beacons_lock); +	processing = pending_reg_beacon(beacon_chan); +	spin_unlock_bh(®_pending_beacons_lock); + +	if (processing)  		return 0;  	reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp);  	if (!reg_beacon)  		return -ENOMEM; -	REG_DBG_PRINT("Found new beacon on " -		      "frequency: %d MHz (Ch %d) on %s\n", +	REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",  		      beacon_chan->center_freq,  		      ieee80211_frequency_to_channel(beacon_chan->center_freq),  		      wiphy_name(wiphy));  	memcpy(®_beacon->chan, beacon_chan, -		sizeof(struct ieee80211_channel)); - +	       sizeof(struct ieee80211_channel));  	/*  	 * Since we can be called from BH or and non-BH context @@ -2156,21 +2058,19 @@ static void print_dfs_region(u8 dfs_region)  		pr_info(" DFS Master region JP");  		break;  	default: -		pr_info(" DFS Master region Uknown"); +		pr_info(" DFS Master region Unknown");  		break;  	}  }  static void print_regdomain(const struct ieee80211_regdomain *rd)  { +	struct regulatory_request *lr = get_last_request();  	if (is_intersected_alpha2(rd->alpha2)) { - -		if (last_request->initiator == -		    NL80211_REGDOM_SET_BY_COUNTRY_IE) { +		if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {  			struct cfg80211_registered_device *rdev; -			rdev = cfg80211_rdev_by_wiphy_idx( -				last_request->wiphy_idx); +			rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx);  			if (rdev) {  				pr_info("Current regulatory domain updated by AP to: %c%c\n",  					rdev->country_ie_alpha2[0], @@ -2179,22 +2079,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)  				pr_info("Current regulatory domain intersected:\n");  		} else  			pr_info("Current regulatory domain intersected:\n"); -	} else if (is_world_regdom(rd->alpha2)) +	} else if (is_world_regdom(rd->alpha2)) {  		pr_info("World regulatory domain updated:\n"); -	else { +	} else {  		if (is_unknown_alpha2(rd->alpha2))  			pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n");  		else { -			if (reg_request_cell_base(last_request)) -				pr_info("Regulatory domain changed " -					"to country: %c%c by Cell Station\n", +			if (reg_request_cell_base(lr)) +				pr_info("Regulatory domain changed to country: %c%c by Cell Station\n",  					rd->alpha2[0], rd->alpha2[1]);  			else -				pr_info("Regulatory domain changed " -					"to country: %c%c\n", +				pr_info("Regulatory domain changed to country: %c%c\n",  					rd->alpha2[0], rd->alpha2[1]);  		}  	} +  	print_dfs_region(rd->dfs_region);  	print_rd_rules(rd);  } @@ -2208,22 +2107,23 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)  /* Takes ownership of rd only if it doesn't fail */  static int __set_regdom(const struct ieee80211_regdomain *rd)  { +	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 (!reg_is_valid_request(rd->alpha2)) +		return -EINVAL; +  	if (is_world_regdom(rd->alpha2)) { -		if (WARN_ON(!reg_is_valid_request(rd->alpha2))) -			return -EINVAL;  		update_world_regdomain(rd);  		return 0;  	}  	if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && -			!is_unknown_alpha2(rd->alpha2)) -		return -EINVAL; - -	if (!last_request) +	    !is_unknown_alpha2(rd->alpha2))  		return -EINVAL;  	/* @@ -2231,7 +2131,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  	 * rd is non static (it means CRDA was present and was used last)  	 * and the pending request came in from a country IE  	 */ -	if (last_request->initiator != NL80211_REGDOM_SET_BY_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 @@ -2247,29 +2147,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  	 * internal EEPROM data  	 */ -	if (WARN_ON(!reg_is_valid_request(rd->alpha2))) -		return -EINVAL; -  	if (!is_valid_rd(rd)) {  		pr_err("Invalid regulatory domain detected:\n");  		print_regdomain_info(rd);  		return -EINVAL;  	} -	request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); +	request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);  	if (!request_wiphy && -	    (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || -	     last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { +	    (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || +	     lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {  		schedule_delayed_work(®_timeout, 0);  		return -ENODEV;  	} -	if (!last_request->intersect) { -		int r; - -		if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { -			reset_regdomains(false); -			cfg80211_regdomain = rd; +	if (!lr->intersect) { +		if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { +			reset_regdomains(false, rd);  			return 0;  		} @@ -2285,20 +2179,19 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  		if (request_wiphy->regd)  			return -EALREADY; -		r = reg_copy_regd(&request_wiphy->regd, rd); -		if (r) -			return r; +		regd = reg_copy_regd(rd); +		if (IS_ERR(regd)) +			return PTR_ERR(regd); -		reset_regdomains(false); -		cfg80211_regdomain = rd; +		rcu_assign_pointer(request_wiphy->regd, regd); +		reset_regdomains(false, rd);  		return 0;  	}  	/* Intersection requires a bit more work */ -	if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - -		intersected_rd = regdom_intersect(rd, cfg80211_regdomain); +	if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { +		intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());  		if (!intersected_rd)  			return -EINVAL; @@ -2307,15 +2200,19 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  		 * However if a driver requested this specific regulatory  		 * domain we keep it for its private use  		 */ -		if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) -			request_wiphy->regd = rd; -		else +		if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) { +			const struct ieee80211_regdomain *tmp; + +			tmp = get_wiphy_regdom(request_wiphy); +			rcu_assign_pointer(request_wiphy->regd, rd); +			rcu_free_regdom(tmp); +		} else {  			kfree(rd); +		}  		rd = NULL; -		reset_regdomains(false); -		cfg80211_regdomain = intersected_rd; +		reset_regdomains(false, intersected_rd);  		return 0;  	} @@ -2327,15 +2224,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  /*   * Use this call to set the current regulatory domain. Conflicts with   * multiple drivers can be ironed out later. Caller must've already - * kmalloc'd the rd structure. Caller must hold cfg80211_mutex + * kmalloc'd the rd structure.   */  int set_regdom(const struct ieee80211_regdomain *rd)  { +	struct regulatory_request *lr;  	int r; -	assert_cfg80211_lock(); -  	mutex_lock(®_mutex); +	lr = get_last_request();  	/* Note that this doesn't update the wiphys, this is done below */  	r = __set_regdom(rd); @@ -2344,51 +2241,52 @@ int set_regdom(const struct ieee80211_regdomain *rd)  			reg_set_request_processed();  		kfree(rd); -		mutex_unlock(®_mutex); -		return r; +		goto out;  	}  	/* This would make this whole thing pointless */ -	if (!last_request->intersect) -		BUG_ON(rd != cfg80211_regdomain); +	if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { +		r = -EINVAL; +		goto out; +	}  	/* update all wiphys now with the new established regulatory domain */ -	update_all_wiphy_regulatory(last_request->initiator); +	update_all_wiphy_regulatory(lr->initiator); -	print_regdomain(cfg80211_regdomain); +	print_regdomain(get_cfg80211_regdom()); -	nl80211_send_reg_change_event(last_request); +	nl80211_send_reg_change_event(lr);  	reg_set_request_processed(); + out:  	mutex_unlock(®_mutex);  	return r;  } -#ifdef CONFIG_HOTPLUG  int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)  { -	if (last_request && !last_request->processed) { -		if (add_uevent_var(env, "COUNTRY=%c%c", -				   last_request->alpha2[0], -				   last_request->alpha2[1])) -			return -ENOMEM; +	struct regulatory_request *lr; +	u8 alpha2[2]; +	bool add = false; + +	rcu_read_lock(); +	lr = get_last_request(); +	if (lr && !lr->processed) { +		memcpy(alpha2, lr->alpha2, 2); +		add = true;  	} +	rcu_read_unlock(); +	if (add) +		return add_uevent_var(env, "COUNTRY=%c%c", +				      alpha2[0], alpha2[1]);  	return 0;  } -#else -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ -	return -ENODEV; -} -#endif /* CONFIG_HOTPLUG */  void wiphy_regulatory_register(struct wiphy *wiphy)  { -	assert_cfg80211_lock(); -  	mutex_lock(®_mutex);  	if (!reg_dev_ignore_cell_hint(wiphy)) @@ -2403,32 +2301,32 @@ void wiphy_regulatory_register(struct wiphy *wiphy)  void wiphy_regulatory_deregister(struct wiphy *wiphy)  {  	struct wiphy *request_wiphy = NULL; - -	assert_cfg80211_lock(); +	struct regulatory_request *lr;  	mutex_lock(®_mutex); +	lr = get_last_request();  	if (!reg_dev_ignore_cell_hint(wiphy))  		reg_num_devs_support_basehint--; -	kfree(wiphy->regd); +	rcu_free_regdom(get_wiphy_regdom(wiphy)); +	rcu_assign_pointer(wiphy->regd, NULL); -	if (last_request) -		request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); +	if (lr) +		request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);  	if (!request_wiphy || request_wiphy != wiphy)  		goto out; -	last_request->wiphy_idx = WIPHY_IDX_STALE; -	last_request->country_ie_env = ENVIRON_ANY; +	lr->wiphy_idx = WIPHY_IDX_INVALID; +	lr->country_ie_env = ENVIRON_ANY;  out:  	mutex_unlock(®_mutex);  }  static void reg_timeout_work(struct work_struct *work)  { -	REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " -		      "restoring regulatory settings\n"); +	REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");  	restore_regulatory_settings(true);  } @@ -2447,13 +2345,13 @@ int __init regulatory_init(void)  	reg_regdb_size_check(); -	cfg80211_regdomain = cfg80211_world_regdom; +	rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom);  	user_alpha2[0] = '9';  	user_alpha2[1] = '7';  	/* We always try to get an update for the static regdomain */ -	err = regulatory_hint_core(cfg80211_regdomain->alpha2); +	err = regulatory_hint_core(cfg80211_world_regdom->alpha2);  	if (err) {  		if (err == -ENOMEM)  			return err; @@ -2465,10 +2363,6 @@ int __init regulatory_init(void)  		 * errors as non-fatal.  		 */  		pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); -#ifdef CONFIG_CFG80211_REG_DEBUG -		/* We want to find out exactly why when debugging */ -		WARN_ON(err); -#endif  	}  	/* @@ -2482,7 +2376,7 @@ int __init regulatory_init(void)  	return 0;  } -void /* __init_or_exit */ regulatory_exit(void) +void regulatory_exit(void)  {  	struct regulatory_request *reg_request, *tmp;  	struct reg_beacon *reg_beacon, *btmp; @@ -2490,43 +2384,27 @@ void /* __init_or_exit */ regulatory_exit(void)  	cancel_work_sync(®_work);  	cancel_delayed_work_sync(®_timeout); -	mutex_lock(&cfg80211_mutex); +	/* Lock to suppress warnings */  	mutex_lock(®_mutex); - -	reset_regdomains(true); +	reset_regdomains(true, NULL); +	mutex_unlock(®_mutex);  	dev_set_uevent_suppress(®_pdev->dev, true);  	platform_device_unregister(reg_pdev); -	spin_lock_bh(®_pending_beacons_lock); -	if (!list_empty(®_pending_beacons)) { -		list_for_each_entry_safe(reg_beacon, btmp, -					 ®_pending_beacons, list) { -			list_del(®_beacon->list); -			kfree(reg_beacon); -		} +	list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { +		list_del(®_beacon->list); +		kfree(reg_beacon);  	} -	spin_unlock_bh(®_pending_beacons_lock); -	if (!list_empty(®_beacon_list)) { -		list_for_each_entry_safe(reg_beacon, btmp, -					 ®_beacon_list, list) { -			list_del(®_beacon->list); -			kfree(reg_beacon); -		} +	list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { +		list_del(®_beacon->list); +		kfree(reg_beacon);  	} -	spin_lock(®_requests_lock); -	if (!list_empty(®_requests_list)) { -		list_for_each_entry_safe(reg_request, tmp, -					 ®_requests_list, list) { -			list_del(®_request->list); -			kfree(reg_request); -		} +	list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { +		list_del(®_request->list); +		kfree(reg_request);  	} -	spin_unlock(®_requests_lock); - -	mutex_unlock(®_mutex); -	mutex_unlock(&cfg80211_mutex);  } diff --git a/net/wireless/reg.h b/net/wireless/reg.h index f023c8a31c6..af2d5f8a5d8 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -16,10 +16,9 @@   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   */ -extern const struct ieee80211_regdomain *cfg80211_regdomain; +extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;  bool is_world_regdom(const char *alpha2); -bool reg_is_valid_request(const char *alpha2);  bool reg_supported_dfs_region(u8 dfs_region);  int regulatory_hint_user(const char *alpha2, @@ -55,8 +54,8 @@ bool reg_last_request_cell_base(void);   * set the wiphy->disable_beacon_hints to true.   */  int regulatory_hint_found_beacon(struct wiphy *wiphy, -					struct ieee80211_channel *beacon_chan, -					gfp_t gfp); +				 struct ieee80211_channel *beacon_chan, +				 gfp_t gfp);  /**   * regulatory_hint_11d - hints a country IE as a regulatory domain @@ -81,7 +80,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,   */  void regulatory_hint_11d(struct wiphy *wiphy,  			 enum ieee80211_band band, -			 u8 *country_ie, +			 const u8 *country_ie,  			 u8 country_ie_len);  /** diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9730c9862bd..674aadca007 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -17,9 +17,150 @@  #include "core.h"  #include "nl80211.h"  #include "wext-compat.h" +#include "rdev-ops.h" + +/** + * DOC: BSS tree/list structure + * + * At the top level, the BSS list is kept in both a list in each + * registered device (@bss_list) as well as an RB-tree for faster + * lookup. In the RB-tree, entries can be looked up using their + * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID + * for other BSSes. + * + * Due to the possibility of hidden SSIDs, there's a second level + * structure, the "hidden_list" and "hidden_beacon_bss" pointer. + * The hidden_list connects all BSSes belonging to a single AP + * that has a hidden SSID, and connects beacon and probe response + * entries. For a probe response entry for a hidden SSID, the + * hidden_beacon_bss pointer points to the BSS struct holding the + * beacon's information. + * + * Reference counting is done for all these references except for + * the hidden_list, so that a beacon BSS struct that is otherwise + * not referenced has one reference for being on the bss_list and + * one for each probe response entry that points to it using the + * hidden_beacon_bss pointer. When a BSS struct that has such a + * pointer is get/put, the refcount update is also propagated to + * the referenced struct, this ensure that it cannot get removed + * while somebody is using the probe response version. + * + * Note that the hidden_beacon_bss pointer never changes, due to + * the reference counting. Therefore, no locking is needed for + * it. + * + * Also note that the hidden_beacon_bss pointer is only relevant + * if the driver uses something other than the IEs, e.g. private + * data stored stored in the BSS struct, since the beacon IEs are + * also linked into the probe response struct. + */  #define IEEE80211_SCAN_RESULT_EXPIRE	(30 * HZ) +static void bss_free(struct cfg80211_internal_bss *bss) +{ +	struct cfg80211_bss_ies *ies; + +	if (WARN_ON(atomic_read(&bss->hold))) +		return; + +	ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); +	if (ies && !bss->pub.hidden_beacon_bss) +		kfree_rcu(ies, rcu_head); +	ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); +	if (ies) +		kfree_rcu(ies, rcu_head); + +	/* +	 * This happens when the module is removed, it doesn't +	 * really matter any more save for completeness +	 */ +	if (!list_empty(&bss->hidden_list)) +		list_del(&bss->hidden_list); + +	kfree(bss); +} + +static inline void bss_ref_get(struct cfg80211_registered_device *dev, +			       struct cfg80211_internal_bss *bss) +{ +	lockdep_assert_held(&dev->bss_lock); + +	bss->refcount++; +	if (bss->pub.hidden_beacon_bss) { +		bss = container_of(bss->pub.hidden_beacon_bss, +				   struct cfg80211_internal_bss, +				   pub); +		bss->refcount++; +	} +} + +static inline void bss_ref_put(struct cfg80211_registered_device *dev, +			       struct cfg80211_internal_bss *bss) +{ +	lockdep_assert_held(&dev->bss_lock); + +	if (bss->pub.hidden_beacon_bss) { +		struct cfg80211_internal_bss *hbss; +		hbss = container_of(bss->pub.hidden_beacon_bss, +				    struct cfg80211_internal_bss, +				    pub); +		hbss->refcount--; +		if (hbss->refcount == 0) +			bss_free(hbss); +	} +	bss->refcount--; +	if (bss->refcount == 0) +		bss_free(bss); +} + +static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, +				  struct cfg80211_internal_bss *bss) +{ +	lockdep_assert_held(&dev->bss_lock); + +	if (!list_empty(&bss->hidden_list)) { +		/* +		 * don't remove the beacon entry if it has +		 * probe responses associated with it +		 */ +		if (!bss->pub.hidden_beacon_bss) +			return false; +		/* +		 * if it's a probe response entry break its +		 * link to the other entries in the group +		 */ +		list_del_init(&bss->hidden_list); +	} + +	list_del_init(&bss->list); +	rb_erase(&bss->rbn, &dev->bss_tree); +	bss_ref_put(dev, bss); +	return true; +} + +static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, +				  unsigned long expire_time) +{ +	struct cfg80211_internal_bss *bss, *tmp; +	bool expired = false; + +	lockdep_assert_held(&dev->bss_lock); + +	list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { +		if (atomic_read(&bss->hold)) +			continue; +		if (!time_after(expire_time, bss->ts)) +			continue; + +		if (__cfg80211_unlink_bss(dev, bss)) +			expired = true; +	} + +	if (expired) +		dev->bss_generation++; +} +  void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)  {  	struct cfg80211_scan_request *request; @@ -45,10 +186,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)  	if (wdev->netdev)  		cfg80211_sme_scan_done(wdev->netdev); -	if (request->aborted) +	if (request->aborted) {  		nl80211_send_scan_aborted(rdev, wdev); -	else +	} else { +		if (request->flags & NL80211_SCAN_FLAG_FLUSH) { +			/* flush entries from previous scans */ +			spin_lock_bh(&rdev->bss_lock); +			__cfg80211_bss_expire(rdev, request->scan_start); +			spin_unlock_bh(&rdev->bss_lock); +		}  		nl80211_send_scan_done(rdev, wdev); +	}  #ifdef CONFIG_CFG80211_WEXT  	if (wdev->netdev && !request->aborted) { @@ -89,6 +237,7 @@ void __cfg80211_scan_done(struct work_struct *wk)  void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)  { +	trace_cfg80211_scan_done(request, aborted);  	WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);  	request->aborted = aborted; @@ -99,22 +248,34 @@ EXPORT_SYMBOL(cfg80211_scan_done);  void __cfg80211_sched_scan_results(struct work_struct *wk)  {  	struct cfg80211_registered_device *rdev; +	struct cfg80211_sched_scan_request *request;  	rdev = container_of(wk, struct cfg80211_registered_device,  			    sched_scan_results_wk); +	request = rdev->sched_scan_req; +  	mutex_lock(&rdev->sched_scan_mtx);  	/* we don't have sched_scan_req anymore if the scan is stopping */ -	if (rdev->sched_scan_req) -		nl80211_send_sched_scan_results(rdev, -						rdev->sched_scan_req->dev); +	if (request) { +		if (request->flags & NL80211_SCAN_FLAG_FLUSH) { +			/* flush entries from previous scans */ +			spin_lock_bh(&rdev->bss_lock); +			__cfg80211_bss_expire(rdev, request->scan_start); +			spin_unlock_bh(&rdev->bss_lock); +			request->scan_start = +				jiffies + msecs_to_jiffies(request->interval); +		} +		nl80211_send_sched_scan_results(rdev, request->dev); +	}  	mutex_unlock(&rdev->sched_scan_mtx);  }  void cfg80211_sched_scan_results(struct wiphy *wiphy)  { +	trace_cfg80211_sched_scan_results(wiphy);  	/* ignore if we're not scanning */  	if (wiphy_to_dev(wiphy)->sched_scan_req)  		queue_work(cfg80211_wq, @@ -126,6 +287,8 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy)  {  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	trace_cfg80211_sched_scan_stopped(wiphy); +  	mutex_lock(&rdev->sched_scan_mtx);  	__cfg80211_stop_sched_scan(rdev, true);  	mutex_unlock(&rdev->sched_scan_mtx); @@ -145,7 +308,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,  	dev = rdev->sched_scan_req->dev;  	if (!driver_initiated) { -		int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); +		int err = rdev_sched_scan_stop(rdev, dev);  		if (err)  			return err;  	} @@ -158,62 +321,21 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,  	return 0;  } -static void bss_release(struct kref *ref) -{ -	struct cfg80211_internal_bss *bss; - -	bss = container_of(ref, struct cfg80211_internal_bss, ref); -	if (bss->pub.free_priv) -		bss->pub.free_priv(&bss->pub); - -	if (bss->beacon_ies_allocated) -		kfree(bss->pub.beacon_ies); -	if (bss->proberesp_ies_allocated) -		kfree(bss->pub.proberesp_ies); - -	BUG_ON(atomic_read(&bss->hold)); - -	kfree(bss); -} - -/* must hold dev->bss_lock! */  void cfg80211_bss_age(struct cfg80211_registered_device *dev,                        unsigned long age_secs)  {  	struct cfg80211_internal_bss *bss;  	unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); -	list_for_each_entry(bss, &dev->bss_list, list) { +	spin_lock_bh(&dev->bss_lock); +	list_for_each_entry(bss, &dev->bss_list, list)  		bss->ts -= age_jiffies; -	} -} - -/* must hold dev->bss_lock! */ -static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, -				  struct cfg80211_internal_bss *bss) -{ -	list_del_init(&bss->list); -	rb_erase(&bss->rbn, &dev->bss_tree); -	kref_put(&bss->ref, bss_release); +	spin_unlock_bh(&dev->bss_lock);  } -/* must hold dev->bss_lock! */  void cfg80211_bss_expire(struct cfg80211_registered_device *dev)  { -	struct cfg80211_internal_bss *bss, *tmp; -	bool expired = false; - -	list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { -		if (atomic_read(&bss->hold)) -			continue; -		if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) -			continue; -		__cfg80211_unlink_bss(dev, bss); -		expired = true; -	} - -	if (expired) -		dev->bss_generation++; +	__cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);  }  const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) @@ -243,44 +365,28 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,  		if (!pos)  			return NULL; -		if (end - pos < sizeof(*ie)) -			return NULL; -  		ie = (struct ieee80211_vendor_ie *)pos; + +		/* make sure we can access ie->len */ +		BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1); + +		if (ie->len < sizeof(*ie)) +			goto cont; +  		ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2];  		if (ie_oui == oui && ie->oui_type == oui_type)  			return pos; - +cont:  		pos += 2 + ie->len;  	}  	return NULL;  }  EXPORT_SYMBOL(cfg80211_find_vendor_ie); -static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) -{ -	const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); -	const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); - -	/* equal if both missing */ -	if (!ie1 && !ie2) -		return 0; -	/* sort missing IE before (left of) present IE */ -	if (!ie1) -		return -1; -	if (!ie2) -		return 1; - -	/* sort by length first, then by contents */ -	if (ie1[1] != ie2[1]) -		return ie2[1] - ie1[1]; -	return memcmp(ie1 + 2, ie2 + 2, ie1[1]); -} - -static bool is_bss(struct cfg80211_bss *a, -		   const u8 *bssid, +static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,  		   const u8 *ssid, size_t ssid_len)  { +	const struct cfg80211_bss_ies *ies;  	const u8 *ssidie;  	if (bssid && !ether_addr_equal(a->bssid, bssid)) @@ -289,9 +395,10 @@ static bool is_bss(struct cfg80211_bss *a,  	if (!ssid)  		return true; -	ssidie = cfg80211_find_ie(WLAN_EID_SSID, -				  a->information_elements, -				  a->len_information_elements); +	ies = rcu_access_pointer(a->ies); +	if (!ies) +		return false; +	ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);  	if (!ssidie)  		return false;  	if (ssidie[1] != ssid_len) @@ -299,133 +406,83 @@ static bool is_bss(struct cfg80211_bss *a,  	return memcmp(ssidie + 2, ssid, ssid_len) == 0;  } -static bool is_mesh_bss(struct cfg80211_bss *a) -{ -	const u8 *ie; - -	if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) -		return false; - -	ie = cfg80211_find_ie(WLAN_EID_MESH_ID, -			      a->information_elements, -			      a->len_information_elements); -	if (!ie) -		return false; - -	ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, -			      a->information_elements, -			      a->len_information_elements); -	if (!ie) -		return false; - -	return true; -} +/** + * enum bss_compare_mode - BSS compare mode + * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) + * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode + * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode + */ +enum bss_compare_mode { +	BSS_CMP_REGULAR, +	BSS_CMP_HIDE_ZLEN, +	BSS_CMP_HIDE_NUL, +}; -static bool is_mesh(struct cfg80211_bss *a, -		    const u8 *meshid, size_t meshidlen, -		    const u8 *meshcfg) +static int cmp_bss(struct cfg80211_bss *a, +		   struct cfg80211_bss *b, +		   enum bss_compare_mode mode)  { -	const u8 *ie; - -	if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) -		return false; +	const struct cfg80211_bss_ies *a_ies, *b_ies; +	const u8 *ie1 = NULL; +	const u8 *ie2 = NULL; +	int i, r; -	ie = cfg80211_find_ie(WLAN_EID_MESH_ID, -			      a->information_elements, -			      a->len_information_elements); -	if (!ie) -		return false; -	if (ie[1] != meshidlen) -		return false; -	if (memcmp(ie + 2, meshid, meshidlen)) -		return false; - -	ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, -			      a->information_elements, -			      a->len_information_elements); -	if (!ie) -		return false; -	if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) -		return false; +	if (a->channel != b->channel) +		return b->channel->center_freq - a->channel->center_freq; -	/* -	 * Ignore mesh capability (last two bytes of the IE) when -	 * comparing since that may differ between stations taking -	 * part in the same mesh. -	 */ -	return memcmp(ie + 2, meshcfg, -	    sizeof(struct ieee80211_meshconf_ie) - 2) == 0; -} +	a_ies = rcu_access_pointer(a->ies); +	if (!a_ies) +		return -1; +	b_ies = rcu_access_pointer(b->ies); +	if (!b_ies) +		return 1; -static int cmp_bss_core(struct cfg80211_bss *a, -			struct cfg80211_bss *b) -{ -	int r; +	if (WLAN_CAPABILITY_IS_STA_BSS(a->capability)) +		ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID, +				       a_ies->data, a_ies->len); +	if (WLAN_CAPABILITY_IS_STA_BSS(b->capability)) +		ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID, +				       b_ies->data, b_ies->len); +	if (ie1 && ie2) { +		int mesh_id_cmp; -	if (a->channel != b->channel) -		return b->channel->center_freq - a->channel->center_freq; +		if (ie1[1] == ie2[1]) +			mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]); +		else +			mesh_id_cmp = ie2[1] - ie1[1]; -	if (is_mesh_bss(a) && is_mesh_bss(b)) { -		r = cmp_ies(WLAN_EID_MESH_ID, -			    a->information_elements, -			    a->len_information_elements, -			    b->information_elements, -			    b->len_information_elements); -		if (r) -			return r; -		return cmp_ies(WLAN_EID_MESH_CONFIG, -			       a->information_elements, -			       a->len_information_elements, -			       b->information_elements, -			       b->len_information_elements); +		ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, +				       a_ies->data, a_ies->len); +		ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, +				       b_ies->data, b_ies->len); +		if (ie1 && ie2) { +			if (mesh_id_cmp) +				return mesh_id_cmp; +			if (ie1[1] != ie2[1]) +				return ie2[1] - ie1[1]; +			return memcmp(ie1 + 2, ie2 + 2, ie1[1]); +		}  	}  	/*  	 * we can't use compare_ether_addr here since we need a < > operator.  	 * The binary return value of compare_ether_addr isn't enough  	 */ -	return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); -} - -static int cmp_bss(struct cfg80211_bss *a, -		   struct cfg80211_bss *b) -{ -	int r; - -	r = cmp_bss_core(a, b); +	r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));  	if (r)  		return r; -	return cmp_ies(WLAN_EID_SSID, -		       a->information_elements, -		       a->len_information_elements, -		       b->information_elements, -		       b->len_information_elements); -} +	ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); +	ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); -static int cmp_hidden_bss(struct cfg80211_bss *a, -		   struct cfg80211_bss *b) -{ -	const u8 *ie1; -	const u8 *ie2; -	int i; -	int r; - -	r = cmp_bss_core(a, b); -	if (r) -		return r; - -	ie1 = cfg80211_find_ie(WLAN_EID_SSID, -			a->information_elements, -			a->len_information_elements); -	ie2 = cfg80211_find_ie(WLAN_EID_SSID, -			b->information_elements, -			b->len_information_elements); +	if (!ie1 && !ie2) +		return 0; -	/* Key comparator must use same algorithm in any rb-tree -	 * search function (order is important), otherwise ordering -	 * of items in the tree is broken and search gives incorrect -	 * results. This code uses same order as cmp_ies() does. */ +	/* +	 * Note that with "hide_ssid", the function returns a match if +	 * the already-present BSS ("b") is a hidden SSID beacon for +	 * the new BSS ("a"). +	 */  	/* sort missing IE before (left of) present IE */  	if (!ie1) @@ -433,20 +490,36 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,  	if (!ie2)  		return 1; -	/* zero-size SSID is used as an indication of the hidden bss */ -	if (!ie2[1]) +	switch (mode) { +	case BSS_CMP_HIDE_ZLEN: +		/* +		 * In ZLEN mode we assume the BSS entry we're +		 * looking for has a zero-length SSID. So if +		 * the one we're looking at right now has that, +		 * return 0. Otherwise, return the difference +		 * in length, but since we're looking for the +		 * 0-length it's really equivalent to returning +		 * the length of the one we're looking at. +		 * +		 * No content comparison is needed as we assume +		 * the content length is zero. +		 */ +		return ie2[1]; +	case BSS_CMP_REGULAR: +	default: +		/* sort by length first, then by contents */ +		if (ie1[1] != ie2[1]) +			return ie2[1] - ie1[1]; +		return memcmp(ie1 + 2, ie2 + 2, ie1[1]); +	case BSS_CMP_HIDE_NUL: +		if (ie1[1] != ie2[1]) +			return ie2[1] - ie1[1]; +		/* this is equivalent to memcmp(zeroes, ie2 + 2, len) */ +		for (i = 0; i < ie2[1]; i++) +			if (ie2[i + 2]) +				return -1;  		return 0; - -	/* sort by length first, then by contents */ -	if (ie1[1] != ie2[1]) -		return ie2[1] - ie1[1]; - -	/* zeroed SSID ie is another indication of a hidden bss */ -	for (i = 0; i < ie2[1]; i++) -		if (ie2[i + 2]) -			return -1; - -	return 0; +	}  }  struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, @@ -459,6 +532,9 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,  	struct cfg80211_internal_bss *bss, *res = NULL;  	unsigned long now = jiffies; +	trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask, +			       capa_val); +  	spin_lock_bh(&dev->bss_lock);  	list_for_each_entry(bss, &dev->bss_list, list) { @@ -472,7 +548,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,  			continue;  		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {  			res = bss; -			kref_get(&res->ref); +			bss_ref_get(dev, res);  			break;  		}  	} @@ -480,38 +556,11 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,  	spin_unlock_bh(&dev->bss_lock);  	if (!res)  		return NULL; +	trace_cfg80211_return_bss(&res->pub);  	return &res->pub;  }  EXPORT_SYMBOL(cfg80211_get_bss); -struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, -				       struct ieee80211_channel *channel, -				       const u8 *meshid, size_t meshidlen, -				       const u8 *meshcfg) -{ -	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); -	struct cfg80211_internal_bss *bss, *res = NULL; - -	spin_lock_bh(&dev->bss_lock); - -	list_for_each_entry(bss, &dev->bss_list, list) { -		if (channel && bss->pub.channel != channel) -			continue; -		if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { -			res = bss; -			kref_get(&res->ref); -			break; -		} -	} - -	spin_unlock_bh(&dev->bss_lock); -	if (!res) -		return NULL; -	return &res->pub; -} -EXPORT_SYMBOL(cfg80211_get_mesh); - -  static void rb_insert_bss(struct cfg80211_registered_device *dev,  			  struct cfg80211_internal_bss *bss)  { @@ -524,7 +573,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,  		parent = *p;  		tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); -		cmp = cmp_bss(&bss->pub, &tbss->pub); +		cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);  		if (WARN_ON(!cmp)) {  			/* will sort of leak this BSS */ @@ -543,7 +592,8 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,  static struct cfg80211_internal_bss *  rb_find_bss(struct cfg80211_registered_device *dev, -	    struct cfg80211_internal_bss *res) +	    struct cfg80211_internal_bss *res, +	    enum bss_compare_mode mode)  {  	struct rb_node *n = dev->bss_tree.rb_node;  	struct cfg80211_internal_bss *bss; @@ -551,7 +601,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,  	while (n) {  		bss = rb_entry(n, struct cfg80211_internal_bss, rbn); -		r = cmp_bss(&res->pub, &bss->pub); +		r = cmp_bss(&res->pub, &bss->pub, mode);  		if (r == 0)  			return bss; @@ -564,177 +614,249 @@ rb_find_bss(struct cfg80211_registered_device *dev,  	return NULL;  } -static struct cfg80211_internal_bss * -rb_find_hidden_bss(struct cfg80211_registered_device *dev, -	    struct cfg80211_internal_bss *res) +static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, +				   struct cfg80211_internal_bss *new)  { -	struct rb_node *n = dev->bss_tree.rb_node; +	const struct cfg80211_bss_ies *ies;  	struct cfg80211_internal_bss *bss; -	int r; +	const u8 *ie; +	int i, ssidlen; +	u8 fold = 0; -	while (n) { -		bss = rb_entry(n, struct cfg80211_internal_bss, rbn); -		r = cmp_hidden_bss(&res->pub, &bss->pub); +	ies = rcu_access_pointer(new->pub.beacon_ies); +	if (WARN_ON(!ies)) +		return false; -		if (r == 0) -			return bss; -		else if (r < 0) -			n = n->rb_left; -		else -			n = n->rb_right; +	ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); +	if (!ie) { +		/* nothing to do */ +		return true;  	} -	return NULL; -} +	ssidlen = ie[1]; +	for (i = 0; i < ssidlen; i++) +		fold |= ie[2 + i]; -static void -copy_hidden_ies(struct cfg80211_internal_bss *res, -		 struct cfg80211_internal_bss *hidden) -{ -	if (unlikely(res->pub.beacon_ies)) -		return; -	if (WARN_ON(!hidden->pub.beacon_ies)) -		return; +	if (fold) { +		/* not a hidden SSID */ +		return true; +	} -	res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); -	if (unlikely(!res->pub.beacon_ies)) -		return; +	/* This is the bad part ... */ -	res->beacon_ies_allocated = true; -	res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; -	memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, -			res->pub.len_beacon_ies); +	list_for_each_entry(bss, &dev->bss_list, list) { +		if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) +			continue; +		if (bss->pub.channel != new->pub.channel) +			continue; +		if (rcu_access_pointer(bss->pub.beacon_ies)) +			continue; +		ies = rcu_access_pointer(bss->pub.ies); +		if (!ies) +			continue; +		ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); +		if (!ie) +			continue; +		if (ssidlen && ie[1] != ssidlen) +			continue; +		/* that would be odd ... */ +		if (bss->pub.beacon_ies) +			continue; +		if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss)) +			continue; +		if (WARN_ON_ONCE(!list_empty(&bss->hidden_list))) +			list_del(&bss->hidden_list); +		/* combine them */ +		list_add(&bss->hidden_list, &new->hidden_list); +		bss->pub.hidden_beacon_bss = &new->pub; +		new->refcount += bss->refcount; +		rcu_assign_pointer(bss->pub.beacon_ies, +				   new->pub.beacon_ies); +	} + +	return true;  }  static struct cfg80211_internal_bss *  cfg80211_bss_update(struct cfg80211_registered_device *dev, -		    struct cfg80211_internal_bss *res) +		    struct cfg80211_internal_bss *tmp)  {  	struct cfg80211_internal_bss *found = NULL; -	/* -	 * The reference to "res" is donated to this function. -	 */ - -	if (WARN_ON(!res->pub.channel)) { -		kref_put(&res->ref, bss_release); +	if (WARN_ON(!tmp->pub.channel))  		return NULL; -	} -	res->ts = jiffies; +	tmp->ts = jiffies;  	spin_lock_bh(&dev->bss_lock); -	found = rb_find_bss(dev, res); +	if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) { +		spin_unlock_bh(&dev->bss_lock); +		return NULL; +	} + +	found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);  	if (found) { -		found->pub.beacon_interval = res->pub.beacon_interval; -		found->pub.tsf = res->pub.tsf; -		found->pub.signal = res->pub.signal; -		found->pub.capability = res->pub.capability; -		found->ts = res->ts; +		found->pub.beacon_interval = tmp->pub.beacon_interval; +		found->pub.signal = tmp->pub.signal; +		found->pub.capability = tmp->pub.capability; +		found->ts = tmp->ts;  		/* Update IEs */ -		if (res->pub.proberesp_ies) { -			size_t used = dev->wiphy.bss_priv_size + sizeof(*res); -			size_t ielen = res->pub.len_proberesp_ies; +		if (rcu_access_pointer(tmp->pub.proberesp_ies)) { +			const struct cfg80211_bss_ies *old; -			if (found->pub.proberesp_ies && -			    !found->proberesp_ies_allocated && -			    ksize(found) >= used + ielen) { -				memcpy(found->pub.proberesp_ies, -				       res->pub.proberesp_ies, ielen); -				found->pub.len_proberesp_ies = ielen; -			} else { -				u8 *ies = found->pub.proberesp_ies; +			old = rcu_access_pointer(found->pub.proberesp_ies); -				if (found->proberesp_ies_allocated) -					ies = krealloc(ies, ielen, GFP_ATOMIC); -				else -					ies = kmalloc(ielen, GFP_ATOMIC); +			rcu_assign_pointer(found->pub.proberesp_ies, +					   tmp->pub.proberesp_ies); +			/* Override possible earlier Beacon frame IEs */ +			rcu_assign_pointer(found->pub.ies, +					   tmp->pub.proberesp_ies); +			if (old) +				kfree_rcu((struct cfg80211_bss_ies *)old, +					  rcu_head); +		} else if (rcu_access_pointer(tmp->pub.beacon_ies)) { +			const struct cfg80211_bss_ies *old; +			struct cfg80211_internal_bss *bss; -				if (ies) { -					memcpy(ies, res->pub.proberesp_ies, -					       ielen); -					found->proberesp_ies_allocated = true; -					found->pub.proberesp_ies = ies; -					found->pub.len_proberesp_ies = ielen; -				} +			if (found->pub.hidden_beacon_bss && +			    !list_empty(&found->hidden_list)) { +				/* +				 * The found BSS struct is one of the probe +				 * response members of a group, but we're +				 * receiving a beacon (beacon_ies in the tmp +				 * bss is used). This can only mean that the +				 * AP changed its beacon from not having an +				 * SSID to showing it, which is confusing so +				 * drop this information. +				 */ +				goto drop;  			} -			/* Override possible earlier Beacon frame IEs */ -			found->pub.information_elements = -				found->pub.proberesp_ies; -			found->pub.len_information_elements = -				found->pub.len_proberesp_ies; -		} -		if (res->pub.beacon_ies) { -			size_t used = dev->wiphy.bss_priv_size + sizeof(*res); -			size_t ielen = res->pub.len_beacon_ies; -			bool information_elements_is_beacon_ies = -				(found->pub.information_elements == -				 found->pub.beacon_ies); +			old = rcu_access_pointer(found->pub.beacon_ies); + +			rcu_assign_pointer(found->pub.beacon_ies, +					   tmp->pub.beacon_ies); -			if (found->pub.beacon_ies && -			    !found->beacon_ies_allocated && -			    ksize(found) >= used + ielen) { -				memcpy(found->pub.beacon_ies, -				       res->pub.beacon_ies, ielen); -				found->pub.len_beacon_ies = ielen; -			} else { -				u8 *ies = found->pub.beacon_ies; +			/* Override IEs if they were from a beacon before */ +			if (old == rcu_access_pointer(found->pub.ies)) +				rcu_assign_pointer(found->pub.ies, +						   tmp->pub.beacon_ies); -				if (found->beacon_ies_allocated) -					ies = krealloc(ies, ielen, GFP_ATOMIC); -				else -					ies = kmalloc(ielen, GFP_ATOMIC); +			/* Assign beacon IEs to all sub entries */ +			list_for_each_entry(bss, &found->hidden_list, +					    hidden_list) { +				const struct cfg80211_bss_ies *ies; -				if (ies) { -					memcpy(ies, res->pub.beacon_ies, -					       ielen); -					found->beacon_ies_allocated = true; -					found->pub.beacon_ies = ies; -					found->pub.len_beacon_ies = ielen; -				} -			} +				ies = rcu_access_pointer(bss->pub.beacon_ies); +				WARN_ON(ies != old); -			/* Override IEs if they were from a beacon before */ -			if (information_elements_is_beacon_ies) { -				found->pub.information_elements = -					found->pub.beacon_ies; -				found->pub.len_information_elements = -					found->pub.len_beacon_ies; +				rcu_assign_pointer(bss->pub.beacon_ies, +						   tmp->pub.beacon_ies);  			} -		} -		kref_put(&res->ref, bss_release); +			if (old) +				kfree_rcu((struct cfg80211_bss_ies *)old, +					  rcu_head); +		}  	} else { +		struct cfg80211_internal_bss *new;  		struct cfg80211_internal_bss *hidden; +		struct cfg80211_bss_ies *ies; -		/* First check if the beacon is a probe response from -		 * a hidden bss. If so, copy beacon ies (with nullified -		 * ssid) into the probe response bss entry (with real ssid). -		 * It is required basically for PSM implementation -		 * (probe responses do not contain tim ie) */ +		/* +		 * create a copy -- the "res" variable that is passed in +		 * is allocated on the stack since it's not needed in the +		 * more common case of an update +		 */ +		new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size, +			      GFP_ATOMIC); +		if (!new) { +			ies = (void *)rcu_dereference(tmp->pub.beacon_ies); +			if (ies) +				kfree_rcu(ies, rcu_head); +			ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); +			if (ies) +				kfree_rcu(ies, rcu_head); +			goto drop; +		} +		memcpy(new, tmp, sizeof(*new)); +		new->refcount = 1; +		INIT_LIST_HEAD(&new->hidden_list); -		/* TODO: The code is not trying to update existing probe -		 * response bss entries when beacon ies are -		 * getting changed. */ -		hidden = rb_find_hidden_bss(dev, res); -		if (hidden) -			copy_hidden_ies(res, hidden); +		if (rcu_access_pointer(tmp->pub.proberesp_ies)) { +			hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); +			if (!hidden) +				hidden = rb_find_bss(dev, tmp, +						     BSS_CMP_HIDE_NUL); +			if (hidden) { +				new->pub.hidden_beacon_bss = &hidden->pub; +				list_add(&new->hidden_list, +					 &hidden->hidden_list); +				hidden->refcount++; +				rcu_assign_pointer(new->pub.beacon_ies, +						   hidden->pub.beacon_ies); +			} +		} else { +			/* +			 * Ok so we found a beacon, and don't have an entry. If +			 * it's a beacon with hidden SSID, we might be in for an +			 * expensive search for any probe responses that should +			 * be grouped with this beacon for updates ... +			 */ +			if (!cfg80211_combine_bsses(dev, new)) { +				kfree(new); +				goto drop; +			} +		} -		/* this "consumes" the reference */ -		list_add_tail(&res->list, &dev->bss_list); -		rb_insert_bss(dev, res); -		found = res; +		list_add_tail(&new->list, &dev->bss_list); +		rb_insert_bss(dev, new); +		found = new;  	}  	dev->bss_generation++; +	bss_ref_get(dev, found);  	spin_unlock_bh(&dev->bss_lock); -	kref_get(&found->ref);  	return found; + drop: +	spin_unlock_bh(&dev->bss_lock); +	return NULL; +} + +static struct ieee80211_channel * +cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, +			 struct ieee80211_channel *channel) +{ +	const u8 *tmp; +	u32 freq; +	int channel_number = -1; + +	tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen); +	if (tmp && tmp[1] == 1) { +		channel_number = tmp[2]; +	} else { +		tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen); +		if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) { +			struct ieee80211_ht_operation *htop = (void *)(tmp + 2); + +			channel_number = htop->primary_chan; +		} +	} + +	if (channel_number < 0) +		return channel; + +	freq = ieee80211_channel_to_frequency(channel_number, channel->band); +	channel = ieee80211_get_channel(wiphy, freq); +	if (!channel) +		return NULL; +	if (channel->flags & IEEE80211_CHAN_DISABLED) +		return NULL; +	return channel;  }  struct cfg80211_bss* @@ -744,54 +866,51 @@ cfg80211_inform_bss(struct wiphy *wiphy,  		    u16 beacon_interval, const u8 *ie, size_t ielen,  		    s32 signal, gfp_t gfp)  { -	struct cfg80211_internal_bss *res; -	size_t privsz; +	struct cfg80211_bss_ies *ies; +	struct cfg80211_internal_bss tmp = {}, *res;  	if (WARN_ON(!wiphy))  		return NULL; -	privsz = wiphy->bss_priv_size; -  	if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&  			(signal < 0 || signal > 100)))  		return NULL; -	res = kzalloc(sizeof(*res) + privsz + ielen, gfp); -	if (!res) +	channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel); +	if (!channel)  		return NULL; -	memcpy(res->pub.bssid, bssid, ETH_ALEN); -	res->pub.channel = channel; -	res->pub.signal = signal; -	res->pub.tsf = tsf; -	res->pub.beacon_interval = beacon_interval; -	res->pub.capability = capability; +	memcpy(tmp.pub.bssid, bssid, ETH_ALEN); +	tmp.pub.channel = channel; +	tmp.pub.signal = signal; +	tmp.pub.beacon_interval = beacon_interval; +	tmp.pub.capability = capability;  	/*  	 * Since we do not know here whether the IEs are from a Beacon or Probe  	 * Response frame, we need to pick one of the options and only use it  	 * with the driver that does not provide the full Beacon/Probe Response  	 * frame. Use Beacon frame pointer to avoid indicating that this should -	 * override the information_elements pointer should we have received an -	 * earlier indication of Probe Response data. -	 * -	 * The initial buffer for the IEs is allocated with the BSS entry and -	 * is located after the private area. +	 * override the IEs pointer should we have received an earlier +	 * indication of Probe Response data.  	 */ -	res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; -	memcpy(res->pub.beacon_ies, ie, ielen); -	res->pub.len_beacon_ies = ielen; -	res->pub.information_elements = res->pub.beacon_ies; -	res->pub.len_information_elements = res->pub.len_beacon_ies; +	ies = kmalloc(sizeof(*ies) + ielen, gfp); +	if (!ies) +		return NULL; +	ies->len = ielen; +	ies->tsf = tsf; +	memcpy(ies->data, ie, ielen); -	kref_init(&res->ref); +	rcu_assign_pointer(tmp.pub.beacon_ies, ies); +	rcu_assign_pointer(tmp.pub.ies, ies); -	res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); +	res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);  	if (!res)  		return NULL;  	if (res->pub.capability & WLAN_CAPABILITY_ESS)  		regulatory_hint_found_beacon(wiphy, channel, gfp); +	trace_cfg80211_return_bss(&res->pub);  	/* cfg80211_bss_update gives us a referenced result */  	return &res->pub;  } @@ -803,10 +922,15 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,  			  struct ieee80211_mgmt *mgmt, size_t len,  			  s32 signal, gfp_t gfp)  { -	struct cfg80211_internal_bss *res; +	struct cfg80211_internal_bss tmp = {}, *res; +	struct cfg80211_bss_ies *ies;  	size_t ielen = len - offsetof(struct ieee80211_mgmt,  				      u.probe_resp.variable); -	size_t privsz; + +	BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != +			offsetof(struct ieee80211_mgmt, u.beacon.variable)); + +	trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);  	if (WARN_ON(!mgmt))  		return NULL; @@ -821,72 +945,72 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,  	if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))  		return NULL; -	privsz = wiphy->bss_priv_size; - -	res = kzalloc(sizeof(*res) + privsz + ielen, gfp); -	if (!res) +	channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, +					   ielen, channel); +	if (!channel)  		return NULL; -	memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); -	res->pub.channel = channel; -	res->pub.signal = signal; -	res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); -	res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); -	res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); -	/* -	 * The initial buffer for the IEs is allocated with the BSS entry and -	 * is located after the private area. -	 */ -	if (ieee80211_is_probe_resp(mgmt->frame_control)) { -		res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; -		memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, -		       ielen); -		res->pub.len_proberesp_ies = ielen; -		res->pub.information_elements = res->pub.proberesp_ies; -		res->pub.len_information_elements = res->pub.len_proberesp_ies; -	} else { -		res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; -		memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); -		res->pub.len_beacon_ies = ielen; -		res->pub.information_elements = res->pub.beacon_ies; -		res->pub.len_information_elements = res->pub.len_beacon_ies; -	} +	ies = kmalloc(sizeof(*ies) + ielen, gfp); +	if (!ies) +		return NULL; +	ies->len = ielen; +	ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); +	memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); -	kref_init(&res->ref); +	if (ieee80211_is_probe_resp(mgmt->frame_control)) +		rcu_assign_pointer(tmp.pub.proberesp_ies, ies); +	else +		rcu_assign_pointer(tmp.pub.beacon_ies, ies); +	rcu_assign_pointer(tmp.pub.ies, ies); +	 +	memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); +	tmp.pub.channel = channel; +	tmp.pub.signal = signal; +	tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); +	tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); -	res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); +	res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);  	if (!res)  		return NULL;  	if (res->pub.capability & WLAN_CAPABILITY_ESS)  		regulatory_hint_found_beacon(wiphy, channel, gfp); +	trace_cfg80211_return_bss(&res->pub);  	/* cfg80211_bss_update gives us a referenced result */  	return &res->pub;  }  EXPORT_SYMBOL(cfg80211_inform_bss_frame); -void cfg80211_ref_bss(struct cfg80211_bss *pub) +void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  { +	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);  	struct cfg80211_internal_bss *bss;  	if (!pub)  		return;  	bss = container_of(pub, struct cfg80211_internal_bss, pub); -	kref_get(&bss->ref); + +	spin_lock_bh(&dev->bss_lock); +	bss_ref_get(dev, bss); +	spin_unlock_bh(&dev->bss_lock);  }  EXPORT_SYMBOL(cfg80211_ref_bss); -void cfg80211_put_bss(struct cfg80211_bss *pub) +void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  { +	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);  	struct cfg80211_internal_bss *bss;  	if (!pub)  		return;  	bss = container_of(pub, struct cfg80211_internal_bss, pub); -	kref_put(&bss->ref, bss_release); + +	spin_lock_bh(&dev->bss_lock); +	bss_ref_put(dev, bss); +	spin_unlock_bh(&dev->bss_lock);  }  EXPORT_SYMBOL(cfg80211_put_bss); @@ -902,8 +1026,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  	spin_lock_bh(&dev->bss_lock);  	if (!list_empty(&bss->list)) { -		__cfg80211_unlink_bss(dev, bss); -		dev->bss_generation++; +		if (__cfg80211_unlink_bss(dev, bss)) +			dev->bss_generation++;  	}  	spin_unlock_bh(&dev->bss_lock);  } @@ -962,6 +1086,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,  	creq->ssids = (void *)&creq->channels[n_channels];  	creq->n_channels = n_channels;  	creq->n_ssids = 1; +	creq->scan_start = jiffies;  	/* translate "Scan on frequencies" request */  	i = 0; @@ -1026,7 +1151,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,  			creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;  	rdev->scan_req = creq; -	err = rdev->ops->scan(wiphy, creq); +	err = rdev_scan(rdev, creq);  	if (err) {  		rdev->scan_req = NULL;  		/* creq will be freed below */ @@ -1044,22 +1169,21 @@ int cfg80211_wext_siwscan(struct net_device *dev,  EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);  static void ieee80211_scan_add_ies(struct iw_request_info *info, -				   struct cfg80211_bss *bss, +				   const struct cfg80211_bss_ies *ies,  				   char **current_ev, char *end_buf)  { -	u8 *pos, *end, *next; +	const u8 *pos, *end, *next;  	struct iw_event iwe; -	if (!bss->information_elements || -	    !bss->len_information_elements) +	if (!ies)  		return;  	/*  	 * If needed, fragment the IEs buffer (at IE boundaries) into short  	 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.  	 */ -	pos = bss->information_elements; -	end = pos + bss->len_information_elements; +	pos = ies->data; +	end = pos + ies->len;  	while (end - pos > IW_GENERIC_IE_MAX) {  		next = pos + 2 + pos[1]; @@ -1070,7 +1194,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,  		iwe.cmd = IWEVGENIE;  		iwe.u.data.length = next - pos;  		*current_ev = iwe_stream_add_point(info, *current_ev, -						   end_buf, &iwe, pos); +						   end_buf, &iwe, +						   (void *)pos);  		pos = next;  	} @@ -1080,29 +1205,21 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,  		iwe.cmd = IWEVGENIE;  		iwe.u.data.length = end - pos;  		*current_ev = iwe_stream_add_point(info, *current_ev, -						   end_buf, &iwe, pos); +						   end_buf, &iwe, +						   (void *)pos);  	}  } -static inline unsigned int elapsed_jiffies_msecs(unsigned long start) -{ -	unsigned long end = jiffies; - -	if (end >= start) -		return jiffies_to_msecs(end - start); - -	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); -} -  static char *  ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  	      struct cfg80211_internal_bss *bss, char *current_ev,  	      char *end_buf)  { +	const struct cfg80211_bss_ies *ies;  	struct iw_event iwe; +	const u8 *ie;  	u8 *buf, *cfg, *p; -	u8 *ie = bss->pub.information_elements; -	int rem = bss->pub.len_information_elements, i, sig; +	int rem, i, sig;  	bool ismesh = false;  	memset(&iwe, 0, sizeof(iwe)); @@ -1167,6 +1284,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  	current_ev = iwe_stream_add_point(info, current_ev, end_buf,  					  &iwe, ""); +	rcu_read_lock(); +	ies = rcu_dereference(bss->pub.ies); +	rem = ies->len; +	ie = ies->data; +  	while (rem >= 2) {  		/* invalid data */  		if (ie[1] > rem - 2) @@ -1179,7 +1301,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  			iwe.u.data.length = ie[1];  			iwe.u.data.flags = 1;  			current_ev = iwe_stream_add_point(info, current_ev, end_buf, -							  &iwe, ie + 2); +							  &iwe, (u8 *)ie + 2);  			break;  		case WLAN_EID_MESH_ID:  			memset(&iwe, 0, sizeof(iwe)); @@ -1187,7 +1309,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  			iwe.u.data.length = ie[1];  			iwe.u.data.flags = 1;  			current_ev = iwe_stream_add_point(info, current_ev, end_buf, -							  &iwe, ie + 2); +							  &iwe, (u8 *)ie + 2);  			break;  		case WLAN_EID_MESH_CONFIG:  			ismesh = true; @@ -1196,7 +1318,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  			buf = kmalloc(50, GFP_ATOMIC);  			if (!buf)  				break; -			cfg = ie + 2; +			cfg = (u8 *)ie + 2;  			memset(&iwe, 0, sizeof(iwe));  			iwe.cmd = IWEVCUSTOM;  			sprintf(buf, "Mesh Network Path Selection Protocol ID: " @@ -1276,11 +1398,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  						  &iwe, IW_EV_UINT_LEN);  	} -	buf = kmalloc(30, GFP_ATOMIC); +	buf = kmalloc(31, GFP_ATOMIC);  	if (buf) {  		memset(&iwe, 0, sizeof(iwe));  		iwe.cmd = IWEVCUSTOM; -		sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); +		sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));  		iwe.u.data.length = strlen(buf);  		current_ev = iwe_stream_add_point(info, current_ev, end_buf,  						  &iwe, buf); @@ -1294,7 +1416,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  		kfree(buf);  	} -	ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); +	ieee80211_scan_add_ies(info, ies, ¤t_ev, end_buf); +	rcu_read_unlock();  	return current_ev;  } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 6f39cb80830..f432bd3755b 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -16,6 +16,7 @@  #include <net/rtnetlink.h>  #include "nl80211.h"  #include "reg.h" +#include "rdev-ops.h"  struct cfg80211_conn {  	struct cfg80211_connect_params params; @@ -138,10 +139,11 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)  	request->wdev = wdev;  	request->wiphy = &rdev->wiphy; +	request->scan_start = jiffies;  	rdev->scan_req = request; -	err = rdev->ops->scan(wdev->wiphy, request); +	err = rdev_scan(rdev, request);  	if (!err) {  		wdev->conn->state = CFG80211_CONN_SCANNING;  		nl80211_send_scan_start(rdev, wdev); @@ -179,7 +181,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)  					    params->ssid, params->ssid_len,  					    NULL, 0,  					    params->key, params->key_len, -					    params->key_idx); +					    params->key_idx, NULL, 0);  	case CFG80211_CONN_ASSOCIATE_NEXT:  		BUG_ON(!rdev->ops->assoc);  		wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -190,7 +192,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)  					    prev_bssid,  					    params->ssid, params->ssid_len,  					    params->ie, params->ie_len, -					    false, ¶ms->crypto, +					    params->mfp != NL80211_MFP_NO, +					    ¶ms->crypto,  					    params->flags, ¶ms->ht_capa,  					    ¶ms->ht_capa_mask);  		if (err) @@ -298,7 +301,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)  	bss = cfg80211_get_conn_bss(wdev);  	if (bss) { -		cfg80211_put_bss(bss); +		cfg80211_put_bss(&rdev->wiphy, bss);  	} else {  		/* not found */  		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) @@ -415,7 +418,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  			       struct cfg80211_bss *bss)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	u8 *country_ie; +	const u8 *country_ie;  #ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  #endif @@ -461,7 +464,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  	if (wdev->current_bss) {  		cfg80211_unhold_bss(wdev->current_bss); -		cfg80211_put_bss(&wdev->current_bss->pub); +		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);  		wdev->current_bss = NULL;  	} @@ -477,7 +480,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  		kfree(wdev->connect_keys);  		wdev->connect_keys = NULL;  		wdev->ssid_len = 0; -		cfg80211_put_bss(bss); +		cfg80211_put_bss(wdev->wiphy, bss);  		return;  	} @@ -499,7 +502,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  	wdev->sme_state = CFG80211_SME_CONNECTED;  	cfg80211_upload_connect_keys(wdev); -	country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); +	rcu_read_lock(); +	country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); +	if (!country_ie) { +		rcu_read_unlock(); +		return; +	} + +	country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC); +	rcu_read_unlock();  	if (!country_ie)  		return; @@ -509,10 +520,9 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  	 * - country_ie + 2, the start of the country ie data, and  	 * - and country_ie[1] which is the IE length  	 */ -	regulatory_hint_11d(wdev->wiphy, -			    bss->channel->band, -			    country_ie + 2, -			    country_ie[1]); +	regulatory_hint_11d(wdev->wiphy, bss->channel->band, +			    country_ie + 2, country_ie[1]); +	kfree(country_ie);  }  void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -576,7 +586,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev,  	}  	cfg80211_unhold_bss(wdev->current_bss); -	cfg80211_put_bss(&wdev->current_bss->pub); +	cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);  	wdev->current_bss = NULL;  	cfg80211_hold_bss(bss_from_pub(bss)); @@ -611,7 +621,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev,  	return;  out: -	cfg80211_put_bss(bss); +	cfg80211_put_bss(wdev->wiphy, bss);  }  void cfg80211_roamed(struct net_device *dev, @@ -653,7 +663,7 @@ void cfg80211_roamed_bss(struct net_device *dev,  	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);  	if (!ev) { -		cfg80211_put_bss(bss); +		cfg80211_put_bss(wdev->wiphy, bss);  		return;  	} @@ -694,7 +704,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,  	if (wdev->current_bss) {  		cfg80211_unhold_bss(wdev->current_bss); -		cfg80211_put_bss(&wdev->current_bss->pub); +		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);  	}  	wdev->current_bss = NULL; @@ -716,7 +726,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,  	 */  	if (rdev->ops->del_key)  		for (i = 0; i < 6; i++) -			rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); +			rdev_del_key(rdev, dev, i, false, NULL);  #ifdef CONFIG_CFG80211_WEXT  	memset(&wrqu, 0, sizeof(wrqu)); @@ -865,7 +875,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,  		if (bss) {  			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;  			err = cfg80211_conn_do_work(wdev); -			cfg80211_put_bss(bss); +			cfg80211_put_bss(wdev->wiphy, bss);  		} else {  			/* otherwise we'll need to scan for the AP first */  			err = cfg80211_conn_scan(wdev); @@ -892,7 +902,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,  	} else {  		wdev->sme_state = CFG80211_SME_CONNECTING;  		wdev->connect_keys = connkeys; -		err = rdev->ops->connect(&rdev->wiphy, dev, connect); +		err = rdev_connect(rdev, dev, connect);  		if (err) {  			wdev->connect_keys = NULL;  			wdev->sme_state = CFG80211_SME_IDLE; @@ -964,7 +974,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,  		if (err)  			return err;  	} else { -		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); +		err = rdev_disconnect(rdev, dev, reason);  		if (err)  			return err;  	} diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index ff574597a85..238ee49b386 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -16,6 +16,7 @@  #include <net/cfg80211.h>  #include "sysfs.h"  #include "core.h" +#include "rdev-ops.h"  static inline struct cfg80211_registered_device *dev_to_rdev(  	struct device *dev) @@ -76,13 +77,11 @@ static void wiphy_dev_release(struct device *dev)  	cfg80211_dev_free(rdev);  } -#ifdef CONFIG_HOTPLUG  static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)  {  	/* TODO, we probably need stuff here */  	return 0;  } -#endif  static int wiphy_suspend(struct device *dev, pm_message_t state)  { @@ -94,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)  	if (rdev->ops->suspend) {  		rtnl_lock();  		if (rdev->wiphy.registered) -			ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); +			ret = rdev_suspend(rdev);  		rtnl_unlock();  	} @@ -107,14 +106,12 @@ static int wiphy_resume(struct device *dev)  	int ret = 0;  	/* Age scan results with time spent in suspend */ -	spin_lock_bh(&rdev->bss_lock);  	cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); -	spin_unlock_bh(&rdev->bss_lock);  	if (rdev->ops->resume) {  		rtnl_lock();  		if (rdev->wiphy.registered) -			ret = rdev->ops->resume(&rdev->wiphy); +			ret = rdev_resume(rdev);  		rtnl_unlock();  	} @@ -133,9 +130,7 @@ struct class ieee80211_class = {  	.owner = THIS_MODULE,  	.dev_release = wiphy_dev_release,  	.dev_attrs = ieee80211_dev_attrs, -#ifdef CONFIG_HOTPLUG  	.dev_uevent = wiphy_uevent, -#endif  	.suspend = wiphy_suspend,  	.resume = wiphy_resume,  	.ns_type = &net_ns_type_operations, diff --git a/net/wireless/trace.c b/net/wireless/trace.c new file mode 100644 index 00000000000..95f997fad75 --- /dev/null +++ b/net/wireless/trace.c @@ -0,0 +1,7 @@ +#include <linux/module.h> + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "trace.h" + +#endif diff --git a/net/wireless/trace.h b/net/wireless/trace.h new file mode 100644 index 00000000000..b7a531380e1 --- /dev/null +++ b/net/wireless/trace.h @@ -0,0 +1,2422 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cfg80211 + +#if !defined(__RDEV_OPS_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __RDEV_OPS_TRACE + +#include <linux/tracepoint.h> + +#include <linux/rtnetlink.h> +#include <net/cfg80211.h> +#include "core.h" + +#define MAC_ENTRY(entry_mac) __array(u8, entry_mac, ETH_ALEN) +#define MAC_ASSIGN(entry_mac, given_mac) do {			     \ +	if (given_mac)						     \ +		memcpy(__entry->entry_mac, given_mac, ETH_ALEN);     \ +	else							     \ +		memset(__entry->entry_mac, 0, ETH_ALEN);	     \ +	} while (0) +#define MAC_PR_FMT "%pM" +#define MAC_PR_ARG(entry_mac) (__entry->entry_mac) + +#define MAXNAME		32 +#define WIPHY_ENTRY	__array(char, wiphy_name, 32) +#define WIPHY_ASSIGN	strlcpy(__entry->wiphy_name, wiphy_name(wiphy), MAXNAME) +#define WIPHY_PR_FMT	"%s" +#define WIPHY_PR_ARG	__entry->wiphy_name + +#define WDEV_ENTRY	__field(u32, id) +#define WDEV_ASSIGN	(__entry->id) = (wdev ? wdev->identifier : 0) +#define WDEV_PR_FMT	"wdev(%u)" +#define WDEV_PR_ARG	(__entry->id) + +#define NETDEV_ENTRY	__array(char, name, IFNAMSIZ) \ +			__field(int, ifindex) +#define NETDEV_ASSIGN					       \ +	do {						       \ +		memcpy(__entry->name, netdev->name, IFNAMSIZ); \ +		(__entry->ifindex) = (netdev->ifindex);	       \ +	} while (0) +#define NETDEV_PR_FMT	"netdev:%s(%d)" +#define NETDEV_PR_ARG	__entry->name, __entry->ifindex + +#define MESH_CFG_ENTRY __field(u16, dot11MeshRetryTimeout)		   \ +		       __field(u16, dot11MeshConfirmTimeout)		   \ +		       __field(u16, dot11MeshHoldingTimeout)		   \ +		       __field(u16, dot11MeshMaxPeerLinks)		   \ +		       __field(u8, dot11MeshMaxRetries)			   \ +		       __field(u8, dot11MeshTTL)			   \ +		       __field(u8, element_ttl)				   \ +		       __field(bool, auto_open_plinks)			   \ +		       __field(u32, dot11MeshNbrOffsetMaxNeighbor)	   \ +		       __field(u8, dot11MeshHWMPmaxPREQretries)		   \ +		       __field(u32, path_refresh_time)			   \ +		       __field(u32, dot11MeshHWMPactivePathTimeout)	   \ +		       __field(u16, min_discovery_timeout)		   \ +		       __field(u16, dot11MeshHWMPpreqMinInterval)	   \ +		       __field(u16, dot11MeshHWMPperrMinInterval)	   \ +		       __field(u16, dot11MeshHWMPnetDiameterTraversalTime) \ +		       __field(u8, dot11MeshHWMPRootMode)		   \ +		       __field(u16, dot11MeshHWMPRannInterval)		   \ +		       __field(bool, dot11MeshGateAnnouncementProtocol)	   \ +		       __field(bool, dot11MeshForwarding)		   \ +		       __field(s32, rssi_threshold)			   \ +		       __field(u16, ht_opmode)				   \ +		       __field(u32, dot11MeshHWMPactivePathToRootTimeout)  \ +		       __field(u16, dot11MeshHWMProotInterval)		   \ +		       __field(u16, dot11MeshHWMPconfirmationInterval) +#define MESH_CFG_ASSIGN							      \ +	do {								      \ +		__entry->dot11MeshRetryTimeout = conf->dot11MeshRetryTimeout; \ +		__entry->dot11MeshConfirmTimeout =			      \ +				conf->dot11MeshConfirmTimeout;		      \ +		__entry->dot11MeshHoldingTimeout =			      \ +				conf->dot11MeshHoldingTimeout;		      \ +		__entry->dot11MeshMaxPeerLinks = conf->dot11MeshMaxPeerLinks; \ +		__entry->dot11MeshMaxRetries = conf->dot11MeshMaxRetries;     \ +		__entry->dot11MeshTTL = conf->dot11MeshTTL;		      \ +		__entry->element_ttl = conf->element_ttl;		      \ +		__entry->auto_open_plinks = conf->auto_open_plinks;	      \ +		__entry->dot11MeshNbrOffsetMaxNeighbor =		      \ +				conf->dot11MeshNbrOffsetMaxNeighbor;	      \ +		__entry->dot11MeshHWMPmaxPREQretries =			      \ +				conf->dot11MeshHWMPmaxPREQretries;	      \ +		__entry->path_refresh_time = conf->path_refresh_time;	      \ +		__entry->dot11MeshHWMPactivePathTimeout =		      \ +				conf->dot11MeshHWMPactivePathTimeout;	      \ +		__entry->min_discovery_timeout = conf->min_discovery_timeout; \ +		__entry->dot11MeshHWMPpreqMinInterval =			      \ +				conf->dot11MeshHWMPpreqMinInterval;	      \ +		__entry->dot11MeshHWMPperrMinInterval =			      \ +				conf->dot11MeshHWMPperrMinInterval;	      \ +		__entry->dot11MeshHWMPnetDiameterTraversalTime =	      \ +				conf->dot11MeshHWMPnetDiameterTraversalTime;  \ +		__entry->dot11MeshHWMPRootMode = conf->dot11MeshHWMPRootMode; \ +		__entry->dot11MeshHWMPRannInterval =			      \ +				conf->dot11MeshHWMPRannInterval;	      \ +		__entry->dot11MeshGateAnnouncementProtocol =		      \ +				conf->dot11MeshGateAnnouncementProtocol;      \ +		__entry->dot11MeshForwarding = conf->dot11MeshForwarding;     \ +		__entry->rssi_threshold = conf->rssi_threshold;		      \ +		__entry->ht_opmode = conf->ht_opmode;			      \ +		__entry->dot11MeshHWMPactivePathToRootTimeout =		      \ +				conf->dot11MeshHWMPactivePathToRootTimeout;   \ +		__entry->dot11MeshHWMProotInterval =			      \ +				conf->dot11MeshHWMProotInterval;	      \ +		__entry->dot11MeshHWMPconfirmationInterval =		      \ +				conf->dot11MeshHWMPconfirmationInterval;      \ +	} while (0) + +#define CHAN_ENTRY __field(enum ieee80211_band, band) \ +		   __field(u16, center_freq) +#define CHAN_ASSIGN(chan)					  \ +	do {							  \ +		if (chan) {					  \ +			__entry->band = chan->band;		  \ +			__entry->center_freq = chan->center_freq; \ +		} else {					  \ +			__entry->band = 0;			  \ +			__entry->center_freq = 0;		  \ +		}						  \ +	} while (0) +#define CHAN_PR_FMT "band: %d, freq: %u" +#define CHAN_PR_ARG __entry->band, __entry->center_freq + +#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band)		\ +		       __field(u32, control_freq)			\ +		       __field(u32, width)				\ +		       __field(u32, center_freq1)			\ +		       __field(u32, center_freq2) +#define CHAN_DEF_ASSIGN(chandef)					\ +	do {								\ +		if ((chandef) && (chandef)->chan) {			\ +			__entry->band = (chandef)->chan->band;		\ +			__entry->control_freq =				\ +				(chandef)->chan->center_freq;		\ +			__entry->width = (chandef)->width;		\ +			__entry->center_freq1 = (chandef)->center_freq1;\ +			__entry->center_freq2 = (chandef)->center_freq2;\ +		} else {						\ +			__entry->band = 0;				\ +			__entry->control_freq = 0;			\ +			__entry->width = 0;				\ +			__entry->center_freq1 = 0;			\ +			__entry->center_freq2 = 0;			\ +		}							\ +	} while (0) +#define CHAN_DEF_PR_FMT							\ +	"band: %d, control freq: %u, width: %d, cf1: %u, cf2: %u" +#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq,		\ +			__entry->width, __entry->center_freq1,		\ +			__entry->center_freq2 + +#define SINFO_ENTRY __field(int, generation)	    \ +		    __field(u32, connected_time)    \ +		    __field(u32, inactive_time)	    \ +		    __field(u32, rx_bytes)	    \ +		    __field(u32, tx_bytes)	    \ +		    __field(u32, rx_packets)	    \ +		    __field(u32, tx_packets)	    \ +		    __field(u32, tx_retries)	    \ +		    __field(u32, tx_failed)	    \ +		    __field(u32, rx_dropped_misc)   \ +		    __field(u32, beacon_loss_count) \ +		    __field(u16, llid)		    \ +		    __field(u16, plid)		    \ +		    __field(u8, plink_state) +#define SINFO_ASSIGN						       \ +	do {							       \ +		__entry->generation = sinfo->generation;	       \ +		__entry->connected_time = sinfo->connected_time;       \ +		__entry->inactive_time = sinfo->inactive_time;	       \ +		__entry->rx_bytes = sinfo->rx_bytes;		       \ +		__entry->tx_bytes = sinfo->tx_bytes;		       \ +		__entry->rx_packets = sinfo->rx_packets;	       \ +		__entry->tx_packets = sinfo->tx_packets;	       \ +		__entry->tx_retries = sinfo->tx_retries;	       \ +		__entry->tx_failed = sinfo->tx_failed;		       \ +		__entry->rx_dropped_misc = sinfo->rx_dropped_misc;     \ +		__entry->beacon_loss_count = sinfo->beacon_loss_count; \ +		__entry->llid = sinfo->llid;			       \ +		__entry->plid = sinfo->plid;			       \ +		__entry->plink_state = sinfo->plink_state;	       \ +	} while (0) + +#define BOOL_TO_STR(bo) (bo) ? "true" : "false" + +/************************************************************* + *			rdev->ops traces		     * + *************************************************************/ + +TRACE_EVENT(rdev_suspend, +	TP_PROTO(struct wiphy *wiphy, struct cfg80211_wowlan *wow), +	TP_ARGS(wiphy, wow), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(bool, any) +		__field(bool, disconnect) +		__field(bool, magic_pkt) +		__field(bool, gtk_rekey_failure) +		__field(bool, eap_identity_req) +		__field(bool, four_way_handshake) +		__field(bool, rfkill_release) +		__field(bool, valid_wow) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		if (wow) { +			__entry->any = wow->any; +			__entry->disconnect = wow->disconnect; +			__entry->magic_pkt = wow->magic_pkt; +			__entry->gtk_rekey_failure = wow->gtk_rekey_failure; +			__entry->eap_identity_req = wow->eap_identity_req; +			__entry->four_way_handshake = wow->four_way_handshake; +			__entry->rfkill_release = wow->rfkill_release; +			__entry->valid_wow = true; +		} else { +			__entry->valid_wow = false; +		} +	), +	TP_printk(WIPHY_PR_FMT ", wow%s - any: %d, disconnect: %d, " +		  "magic pkt: %d, gtk rekey failure: %d, eap identify req: %d, " +		  "four way handshake: %d, rfkill release: %d.", +		  WIPHY_PR_ARG, __entry->valid_wow ? "" : "(Not configured!)", +		  __entry->any, __entry->disconnect, __entry->magic_pkt, +		  __entry->gtk_rekey_failure, __entry->eap_identity_req, +		  __entry->four_way_handshake, __entry->rfkill_release) +); + +TRACE_EVENT(rdev_return_int, +	TP_PROTO(struct wiphy *wiphy, int ret), +	TP_ARGS(wiphy, ret), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, ret) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->ret = ret; +	), +	TP_printk(WIPHY_PR_FMT ", returned: %d", WIPHY_PR_ARG, __entry->ret) +); + +TRACE_EVENT(rdev_scan, +	TP_PROTO(struct wiphy *wiphy, struct cfg80211_scan_request *request), +	TP_ARGS(wiphy, request), +	TP_STRUCT__entry( +		WIPHY_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); + +DECLARE_EVENT_CLASS(wiphy_only_evt, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy), +	TP_STRUCT__entry( +		WIPHY_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_resume, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_return_void, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy) +); + +DECLARE_EVENT_CLASS(wiphy_enabled_evt, +	TP_PROTO(struct wiphy *wiphy, bool enabled), +	TP_ARGS(wiphy, enabled), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(bool, enabled) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->enabled = enabled; +	), +	TP_printk(WIPHY_PR_FMT ", %senabled ", +		  WIPHY_PR_ARG, __entry->enabled ? "" : "not ") +); + +DEFINE_EVENT(wiphy_enabled_evt, rdev_set_wakeup, +	TP_PROTO(struct wiphy *wiphy, bool enabled), +	TP_ARGS(wiphy, enabled) +); + +TRACE_EVENT(rdev_add_virtual_intf, +	TP_PROTO(struct wiphy *wiphy, char *name, enum nl80211_iftype type), +	TP_ARGS(wiphy, name, type), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__string(vir_intf_name, name ? name : "<noname>") +		__field(enum nl80211_iftype, type) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__assign_str(vir_intf_name, name ? name : "<noname>"); +		__entry->type = type; +	), +	TP_printk(WIPHY_PR_FMT ", virtual intf name: %s, type: %d", +		  WIPHY_PR_ARG, __get_str(vir_intf_name), __entry->type) +); + +DECLARE_EVENT_CLASS(wiphy_wdev_evt, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), +	TP_ARGS(wiphy, wdev), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WDEV_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), +	TP_ARGS(wiphy, wdev) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_del_virtual_intf, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), +	TP_ARGS(wiphy, wdev) +); + +TRACE_EVENT(rdev_change_virtual_intf, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 enum nl80211_iftype type), +	TP_ARGS(wiphy, netdev, type), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(enum nl80211_iftype, type) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->type = type; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", type: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->type) +); + +DECLARE_EVENT_CLASS(key_handle, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, +		 bool pairwise, const u8 *mac_addr), +	TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(mac_addr) +		__field(u8, key_index) +		__field(bool, pairwise) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(mac_addr, mac_addr); +		__entry->key_index = key_index; +		__entry->pairwise = pairwise; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index, +		  BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr)) +); + +DEFINE_EVENT(key_handle, rdev_add_key, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, +		 bool pairwise, const u8 *mac_addr), +	TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr) +); + +DEFINE_EVENT(key_handle, rdev_get_key, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, +		 bool pairwise, const u8 *mac_addr), +	TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr) +); + +DEFINE_EVENT(key_handle, rdev_del_key, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, +		 bool pairwise, const u8 *mac_addr), +	TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr) +); + +TRACE_EVENT(rdev_set_default_key, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, +		 bool unicast, bool multicast), +	TP_ARGS(wiphy, netdev, key_index, unicast, multicast), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(u8, key_index) +		__field(bool, unicast) +		__field(bool, multicast) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->key_index = key_index; +		__entry->unicast = unicast; +		__entry->multicast = multicast; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index, +		  BOOL_TO_STR(__entry->unicast), +		  BOOL_TO_STR(__entry->multicast)) +); + +TRACE_EVENT(rdev_set_default_mgmt_key, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index), +	TP_ARGS(wiphy, netdev, key_index), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(u8, key_index) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->key_index = key_index; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index) +); + +TRACE_EVENT(rdev_start_ap, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_ap_settings *settings), +	TP_ARGS(wiphy, netdev, settings), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		CHAN_DEF_ENTRY +		__field(int, beacon_interval) +		__field(int, dtim_period) +		__array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) +		__field(enum nl80211_hidden_ssid, hidden_ssid) +		__field(u32, wpa_ver) +		__field(bool, privacy) +		__field(enum nl80211_auth_type, auth_type) +		__field(int, inactivity_timeout) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		CHAN_DEF_ASSIGN(&settings->chandef); +		__entry->beacon_interval = settings->beacon_interval; +		__entry->dtim_period = settings->dtim_period; +		__entry->hidden_ssid = settings->hidden_ssid; +		__entry->wpa_ver = settings->crypto.wpa_versions; +		__entry->privacy = settings->privacy; +		__entry->auth_type = settings->auth_type; +		__entry->inactivity_timeout = settings->inactivity_timeout; +		memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); +		memcpy(__entry->ssid, settings->ssid, settings->ssid_len); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", AP settings - ssid: %s, " +		  CHAN_DEF_PR_FMT ", beacon interval: %d, dtim period: %d, " +		  "hidden ssid: %d, wpa versions: %u, privacy: %s, " +		  "auth type: %d, inactivity timeout: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_DEF_PR_ARG, +		  __entry->beacon_interval, __entry->dtim_period, +		  __entry->hidden_ssid, __entry->wpa_ver, +		  BOOL_TO_STR(__entry->privacy), __entry->auth_type, +		  __entry->inactivity_timeout) +); + +TRACE_EVENT(rdev_change_beacon, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_beacon_data *info), +	TP_ARGS(wiphy, netdev, info), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__dynamic_array(u8, head, info ? info->head_len : 0) +		__dynamic_array(u8, tail, info ? info->tail_len : 0) +		__dynamic_array(u8, beacon_ies, info ? info->beacon_ies_len : 0) +		__dynamic_array(u8, proberesp_ies, +				info ? info->proberesp_ies_len : 0) +		__dynamic_array(u8, assocresp_ies, +				info ? info->assocresp_ies_len : 0) +		__dynamic_array(u8, probe_resp, info ? info->probe_resp_len : 0) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		if (info) { +			if (info->head) +				memcpy(__get_dynamic_array(head), info->head, +				       info->head_len); +			if (info->tail) +				memcpy(__get_dynamic_array(tail), info->tail, +				       info->tail_len); +			if (info->beacon_ies) +				memcpy(__get_dynamic_array(beacon_ies), +				       info->beacon_ies, info->beacon_ies_len); +			if (info->proberesp_ies) +				memcpy(__get_dynamic_array(proberesp_ies), +				       info->proberesp_ies, +				       info->proberesp_ies_len); +			if (info->assocresp_ies) +				memcpy(__get_dynamic_array(assocresp_ies), +				       info->assocresp_ies, +				       info->assocresp_ies_len); +			if (info->probe_resp) +				memcpy(__get_dynamic_array(probe_resp), +				       info->probe_resp, info->probe_resp_len); +		} +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +DECLARE_EVENT_CLASS(wiphy_netdev_evt, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_set_rekey_data, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_get_mesh_config, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_mesh, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), +	TP_ARGS(wiphy, netdev) +); + +DECLARE_EVENT_CLASS(station_add_change, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac, +		 struct station_parameters *params), +	TP_ARGS(wiphy, netdev, mac, params), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(sta_mac) +		__field(u32, sta_flags_mask) +		__field(u32, sta_flags_set) +		__field(u32, sta_modify_mask) +		__field(int, listen_interval) +		__field(u16, aid) +		__field(u8, plink_action) +		__field(u8, plink_state) +		__field(u8, uapsd_queues) +		__array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap)) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(sta_mac, mac); +		__entry->sta_flags_mask = params->sta_flags_mask; +		__entry->sta_flags_set = params->sta_flags_set; +		__entry->sta_modify_mask = params->sta_modify_mask; +		__entry->listen_interval = params->listen_interval; +		__entry->aid = params->aid; +		__entry->plink_action = params->plink_action; +		__entry->plink_state = params->plink_state; +		__entry->uapsd_queues = params->uapsd_queues; +		memset(__entry->ht_capa, 0, sizeof(struct ieee80211_ht_cap)); +		if (params->ht_capa) +			memcpy(__entry->ht_capa, params->ht_capa, +			       sizeof(struct ieee80211_ht_cap)); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT +		  ", station flags mask: %u, station flags set: %u, " +		  "station modify mask: %u, listen interval: %d, aid: %u, " +		  "plink action: %u, plink state: %u, uapsd queues: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), +		  __entry->sta_flags_mask, __entry->sta_flags_set, +		  __entry->sta_modify_mask, __entry->listen_interval, +		  __entry->aid, __entry->plink_action, __entry->plink_state, +		  __entry->uapsd_queues) +); + +DEFINE_EVENT(station_add_change, rdev_add_station, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac, +		 struct station_parameters *params), +	TP_ARGS(wiphy, netdev, mac, params) +); + +DEFINE_EVENT(station_add_change, rdev_change_station, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac, +		 struct station_parameters *params), +	TP_ARGS(wiphy, netdev, mac, params) +); + +DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), +	TP_ARGS(wiphy, netdev, mac), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(sta_mac) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(sta_mac, mac); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mac: " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac)) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), +	TP_ARGS(wiphy, netdev, mac) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), +	TP_ARGS(wiphy, netdev, mac) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_mpath, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), +	TP_ARGS(wiphy, netdev, mac) +); + +DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_set_wds_peer, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), +	TP_ARGS(wiphy, netdev, mac) +); + +TRACE_EVENT(rdev_dump_station, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, +		 u8 *mac), +	TP_ARGS(wiphy, netdev, idx, mac), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(sta_mac) +		__field(int, idx) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(sta_mac, mac); +		__entry->idx = idx; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), +		  __entry->idx) +); + +TRACE_EVENT(rdev_return_int_station_info, +	TP_PROTO(struct wiphy *wiphy, int ret, struct station_info *sinfo), +	TP_ARGS(wiphy, ret, sinfo), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, ret) +		SINFO_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->ret = ret; +		SINFO_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT ", returned %d" , +		  WIPHY_PR_ARG, __entry->ret) +); + +DECLARE_EVENT_CLASS(mpath_evt, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, +		 u8 *next_hop), +	TP_ARGS(wiphy, netdev, dst, next_hop), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(dst) +		MAC_ENTRY(next_hop) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(dst, dst); +		MAC_ASSIGN(next_hop, next_hop); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dst), +		  MAC_PR_ARG(next_hop)) +); + +DEFINE_EVENT(mpath_evt, rdev_add_mpath, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, +		 u8 *next_hop), +	TP_ARGS(wiphy, netdev, dst, next_hop) +); + +DEFINE_EVENT(mpath_evt, rdev_change_mpath, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, +		 u8 *next_hop), +	TP_ARGS(wiphy, netdev, dst, next_hop) +); + +DEFINE_EVENT(mpath_evt, rdev_get_mpath, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst, +		 u8 *next_hop), +	TP_ARGS(wiphy, netdev, dst, next_hop) +); + +TRACE_EVENT(rdev_dump_mpath, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, +		 u8 *dst, u8 *next_hop), +	TP_ARGS(wiphy, netdev, idx, dst, next_hop), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(dst) +		MAC_ENTRY(next_hop) +		__field(int, idx) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(dst, dst); +		MAC_ASSIGN(next_hop, next_hop); +		__entry->idx = idx; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: " +		  MAC_PR_FMT ", next hop: " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst), +		  MAC_PR_ARG(next_hop)) +); + +TRACE_EVENT(rdev_return_int_mpath_info, +	TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo), +	TP_ARGS(wiphy, ret, pinfo), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, ret) +		__field(int, generation) +		__field(u32, filled) +		__field(u32, frame_qlen) +		__field(u32, sn) +		__field(u32, metric) +		__field(u32, exptime) +		__field(u32, discovery_timeout) +		__field(u8, discovery_retries) +		__field(u8, flags) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->ret = ret; +		__entry->generation = pinfo->generation; +		__entry->filled = pinfo->filled; +		__entry->frame_qlen = pinfo->frame_qlen; +		__entry->sn = pinfo->sn; +		__entry->metric = pinfo->metric; +		__entry->exptime = pinfo->exptime; +		__entry->discovery_timeout = pinfo->discovery_timeout; +		__entry->discovery_retries = pinfo->discovery_retries; +		__entry->flags = pinfo->flags; +	), +	TP_printk(WIPHY_PR_FMT ", returned %d. mpath info - generation: %d, " +		  "filled: %u, frame qlen: %u, sn: %u, metric: %u, exptime: %u," +		  " discovery timeout: %u, discovery retries: %u, flags: %u", +		  WIPHY_PR_ARG, __entry->ret, __entry->generation, +		  __entry->filled, __entry->frame_qlen, __entry->sn, +		  __entry->metric, __entry->exptime, __entry->discovery_timeout, +		  __entry->discovery_retries, __entry->flags) +); + +TRACE_EVENT(rdev_return_int_mesh_config, +	TP_PROTO(struct wiphy *wiphy, int ret, struct mesh_config *conf), +	TP_ARGS(wiphy, ret, conf), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		MESH_CFG_ENTRY +		__field(int, ret) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		MESH_CFG_ASSIGN; +		__entry->ret = ret; +	), +	TP_printk(WIPHY_PR_FMT ", returned: %d", +		  WIPHY_PR_ARG, __entry->ret) +); + +TRACE_EVENT(rdev_update_mesh_config, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 mask, +		 const struct mesh_config *conf), +	TP_ARGS(wiphy, netdev, mask, conf), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MESH_CFG_ENTRY +		__field(u32, mask) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MESH_CFG_ASSIGN; +		__entry->mask = mask; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mask: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->mask) +); + +TRACE_EVENT(rdev_join_mesh, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 const struct mesh_config *conf, +		 const struct mesh_setup *setup), +	TP_ARGS(wiphy, netdev, conf, setup), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MESH_CFG_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MESH_CFG_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +TRACE_EVENT(rdev_change_bss, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct bss_parameters *params), +	TP_ARGS(wiphy, netdev, params), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(int, use_cts_prot) +		__field(int, use_short_preamble) +		__field(int, use_short_slot_time) +		__field(int, ap_isolate) +		__field(int, ht_opmode) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->use_cts_prot = params->use_cts_prot; +		__entry->use_short_preamble = params->use_short_preamble; +		__entry->use_short_slot_time = params->use_short_slot_time; +		__entry->ap_isolate = params->ap_isolate; +		__entry->ht_opmode = params->ht_opmode; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", use cts prot: %d, " +		  "use short preamble: %d, use short slot time: %d, " +		  "ap isolate: %d, ht opmode: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->use_cts_prot, +		  __entry->use_short_preamble, __entry->use_short_slot_time, +		  __entry->ap_isolate, __entry->ht_opmode) +); + +TRACE_EVENT(rdev_set_txq_params, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct ieee80211_txq_params *params), +	TP_ARGS(wiphy, netdev, params), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(enum nl80211_ac, ac) +		__field(u16, txop) +		__field(u16, cwmin) +		__field(u16, cwmax) +		__field(u8, aifs) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->ac = params->ac; +		__entry->txop = params->txop; +		__entry->cwmin = params->cwmin; +		__entry->cwmax = params->cwmax; +		__entry->aifs = params->aifs; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ac, __entry->txop, +		  __entry->cwmin, __entry->cwmax, __entry->aifs) +); + +TRACE_EVENT(rdev_libertas_set_mesh_channel, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct ieee80211_channel *chan), +	TP_ARGS(wiphy, netdev, chan), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		CHAN_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		CHAN_ASSIGN(chan); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_PR_FMT, WIPHY_PR_ARG, +		  NETDEV_PR_ARG, CHAN_PR_ARG) +); + +TRACE_EVENT(rdev_set_monitor_channel, +	TP_PROTO(struct wiphy *wiphy, +		 struct cfg80211_chan_def *chandef), +	TP_ARGS(wiphy, chandef), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		CHAN_DEF_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		CHAN_DEF_ASSIGN(chandef); +	), +	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, +		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); + +TRACE_EVENT(rdev_auth, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_auth_request *req), +	TP_ARGS(wiphy, netdev, req), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		__field(enum nl80211_auth_type, auth_type) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		if (req->bss) +			MAC_ASSIGN(bssid, req->bss->bssid); +		else +			memset(__entry->bssid, 0, ETH_ALEN); +		__entry->auth_type = req->auth_type; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->auth_type, +		  MAC_PR_ARG(bssid)) +); + +TRACE_EVENT(rdev_assoc, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_assoc_request *req), +	TP_ARGS(wiphy, netdev, req), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		MAC_ENTRY(prev_bssid) +		__field(bool, use_mfp) +		__field(u32, flags) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		if (req->bss) +			MAC_ASSIGN(bssid, req->bss->bssid); +		else +			memset(__entry->bssid, 0, ETH_ALEN); +		MAC_ASSIGN(prev_bssid, req->prev_bssid); +		__entry->use_mfp = req->use_mfp; +		__entry->flags = req->flags; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT +		  ", previous bssid: " MAC_PR_FMT ", use mfp: %s, flags: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), +		  MAC_PR_ARG(prev_bssid), BOOL_TO_STR(__entry->use_mfp), +		  __entry->flags) +); + +TRACE_EVENT(rdev_deauth, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_deauth_request *req), +	TP_ARGS(wiphy, netdev, req), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		__field(u16, reason_code) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(bssid, req->bssid); +		__entry->reason_code = req->reason_code; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), +		  __entry->reason_code) +); + +TRACE_EVENT(rdev_disassoc, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_disassoc_request *req), +	TP_ARGS(wiphy, netdev, req), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		__field(u16, reason_code) +		__field(bool, local_state_change) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		if (req->bss) +			MAC_ASSIGN(bssid, req->bss->bssid); +		else +			memset(__entry->bssid, 0, ETH_ALEN); +		__entry->reason_code = req->reason_code; +		__entry->local_state_change = req->local_state_change; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT +		  ", reason: %u, local state change: %s", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), +		  __entry->reason_code, +		  BOOL_TO_STR(__entry->local_state_change)) +); + +TRACE_EVENT(rdev_mgmt_tx_cancel_wait, +	TP_PROTO(struct wiphy *wiphy, +		 struct wireless_dev *wdev, u64 cookie), +	TP_ARGS(wiphy, wdev, cookie), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +		__field(u64, cookie) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WDEV_ASSIGN; +		__entry->cookie = cookie; +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu ", +		  WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) +); + +TRACE_EVENT(rdev_set_power_mgmt, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 bool enabled, int timeout), +	TP_ARGS(wiphy, netdev, enabled, timeout), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(bool, enabled) +		__field(int, timeout) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->enabled = enabled; +		__entry->timeout = timeout; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %senabled, timeout: %d ", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, +		  __entry->enabled ? "" : "not ", __entry->timeout) +); + +TRACE_EVENT(rdev_connect, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_connect_params *sme), +	TP_ARGS(wiphy, netdev, sme), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		__array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) +		__field(enum nl80211_auth_type, auth_type) +		__field(bool, privacy) +		__field(u32, wpa_versions) +		__field(u32, flags) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(bssid, sme->bssid); +		memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); +		memcpy(__entry->ssid, sme->ssid, sme->ssid_len); +		__entry->auth_type = sme->auth_type; +		__entry->privacy = sme->privacy; +		__entry->wpa_versions = sme->crypto.wpa_versions; +		__entry->flags = sme->flags; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT +		  ", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, " +		  "flags: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid, +		  __entry->auth_type, BOOL_TO_STR(__entry->privacy), +		  __entry->wpa_versions, __entry->flags) +); + +TRACE_EVENT(rdev_set_cqm_rssi_config, +	TP_PROTO(struct wiphy *wiphy, +		 struct net_device *netdev, s32 rssi_thold, +		 u32 rssi_hyst), +	TP_ARGS(wiphy, netdev, rssi_thold, rssi_hyst), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(s32, rssi_thold) +		__field(u32, rssi_hyst) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->rssi_thold = rssi_thold; +		__entry->rssi_hyst = rssi_hyst; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT +		  ", rssi_thold: %d, rssi_hyst: %u ", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, +		 __entry->rssi_thold, __entry->rssi_hyst) +); + +TRACE_EVENT(rdev_set_cqm_txe_config, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate, +		 u32 pkts, u32 intvl), +	TP_ARGS(wiphy, netdev, rate, pkts, intvl), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(u32, rate) +		__field(u32, pkts) +		__field(u32, intvl) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->rate = rate; +		__entry->pkts = pkts; +		__entry->intvl = intvl; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->rate, __entry->pkts, +		  __entry->intvl) +); + +TRACE_EVENT(rdev_disconnect, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 u16 reason_code), +	TP_ARGS(wiphy, netdev, reason_code), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(u16, reason_code) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->reason_code = reason_code; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG, +		  NETDEV_PR_ARG, __entry->reason_code) +); + +TRACE_EVENT(rdev_join_ibss, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_ibss_params *params), +	TP_ARGS(wiphy, netdev, params), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		__array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(bssid, params->bssid); +		memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); +		memcpy(__entry->ssid, params->ssid, params->ssid_len); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid) +); + +TRACE_EVENT(rdev_set_wiphy_params, +	TP_PROTO(struct wiphy *wiphy, u32 changed), +	TP_ARGS(wiphy, changed), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(u32, changed) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->changed = changed; +	), +	TP_printk(WIPHY_PR_FMT ", changed: %u", +		  WIPHY_PR_ARG, __entry->changed) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), +	TP_ARGS(wiphy, wdev) +); + +TRACE_EVENT(rdev_set_tx_power, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, +		 enum nl80211_tx_power_setting type, int mbm), +	TP_ARGS(wiphy, wdev, type, mbm), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +		__field(enum nl80211_tx_power_setting, type) +		__field(int, mbm) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WDEV_ASSIGN; +		__entry->type = type; +		__entry->mbm = mbm; +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type: %u, mbm: %d", +		  WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm) +); + +TRACE_EVENT(rdev_return_int_int, +	TP_PROTO(struct wiphy *wiphy, int func_ret, int func_fill), +	TP_ARGS(wiphy, func_ret, func_fill), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, func_ret) +		__field(int, func_fill) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->func_ret = func_ret; +		__entry->func_fill = func_fill; +	), +	TP_printk(WIPHY_PR_FMT ", function returns: %d, function filled: %d", +		  WIPHY_PR_ARG, __entry->func_ret, __entry->func_fill) +); + +#ifdef CONFIG_NL80211_TESTMODE +TRACE_EVENT(rdev_testmode_cmd, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy), +	TP_STRUCT__entry( +		WIPHY_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); + +TRACE_EVENT(rdev_testmode_dump, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy), +	TP_STRUCT__entry( +		WIPHY_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) +); +#endif /* CONFIG_NL80211_TESTMODE */ + +TRACE_EVENT(rdev_set_bitrate_mask, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 const u8 *peer, const struct cfg80211_bitrate_mask *mask), +	TP_ARGS(wiphy, netdev, peer, mask), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(peer) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(peer, peer); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer)) +); + +TRACE_EVENT(rdev_mgmt_frame_register, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, +		 u16 frame_type, bool reg), +	TP_ARGS(wiphy, wdev, frame_type, reg), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +		__field(u16, frame_type) +		__field(bool, reg) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WDEV_ASSIGN; +		__entry->frame_type = frame_type; +		__entry->reg = reg; +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", frame_type: 0x%.2x, reg: %s ", +		  WIPHY_PR_ARG, WDEV_PR_ARG, __entry->frame_type, +		  __entry->reg ? "true" : "false") +); + +TRACE_EVENT(rdev_return_int_tx_rx, +	TP_PROTO(struct wiphy *wiphy, int ret, u32 tx, u32 rx), +	TP_ARGS(wiphy, ret, tx, rx), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, ret) +		__field(u32, tx) +		__field(u32, rx) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->ret = ret; +		__entry->tx = tx; +		__entry->rx = rx; +	), +	TP_printk(WIPHY_PR_FMT ", returned %d, tx: %u, rx: %u", +		  WIPHY_PR_ARG, __entry->ret, __entry->tx, __entry->rx) +); + +TRACE_EVENT(rdev_return_void_tx_rx, +	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 tx_max, +		 u32 rx, u32 rx_max), +	TP_ARGS(wiphy, tx, tx_max, rx, rx_max), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(u32, tx) +		__field(u32, tx_max) +		__field(u32, rx) +		__field(u32, rx_max) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->tx = tx; +		__entry->tx_max = tx_max; +		__entry->rx = rx; +		__entry->rx_max = rx_max; +	), +	TP_printk(WIPHY_PR_FMT ", tx: %u, tx_max: %u, rx: %u, rx_max: %u ", +		  WIPHY_PR_ARG, __entry->tx, __entry->tx_max, __entry->rx, +		  __entry->rx_max) +); + +DECLARE_EVENT_CLASS(tx_rx_evt, +	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), +	TP_ARGS(wiphy, rx, tx), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(u32, tx) +		__field(u32, rx) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->tx = tx; +		__entry->rx = rx; +	), +	TP_printk(WIPHY_PR_FMT ", tx: %u, rx: %u ", +		  WIPHY_PR_ARG, __entry->tx, __entry->rx) +); + +DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam, +	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), +	TP_ARGS(wiphy, rx, tx) +); + +DEFINE_EVENT(tx_rx_evt, rdev_set_antenna, +	TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), +	TP_ARGS(wiphy, rx, tx) +); + +TRACE_EVENT(rdev_sched_scan_start, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_sched_scan_request *request), +	TP_ARGS(wiphy, netdev, request), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG) +); + +TRACE_EVENT(rdev_tdls_mgmt, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 u8 *peer, u8 action_code, u8 dialog_token, +		 u16 status_code, const u8 *buf, size_t len), +	TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code, +		buf, len), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(peer) +		__field(u8, action_code) +		__field(u8, dialog_token) +		__field(u16, status_code) +		__dynamic_array(u8, buf, len) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(peer, peer); +		__entry->action_code = action_code; +		__entry->dialog_token = dialog_token; +		__entry->status_code = status_code; +		memcpy(__get_dynamic_array(buf), buf, len); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, " +		  "dialog_token: %u, status_code: %u, buf: %#.2x ", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), +		  __entry->action_code, __entry->dialog_token, +		  __entry->status_code, ((u8 *)__get_dynamic_array(buf))[0]) +); + +TRACE_EVENT(rdev_dump_survey, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx), +	TP_ARGS(wiphy, netdev, idx), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(int, idx) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->idx = idx; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx) +); + +TRACE_EVENT(rdev_return_int_survey_info, +	TP_PROTO(struct wiphy *wiphy, int ret, struct survey_info *info), +	TP_ARGS(wiphy, ret, info), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		CHAN_ENTRY +		__field(int, ret) +		__field(u64, channel_time) +		__field(u64, channel_time_busy) +		__field(u64, channel_time_ext_busy) +		__field(u64, channel_time_rx) +		__field(u64, channel_time_tx) +		__field(u32, filled) +		__field(s8, noise) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		CHAN_ASSIGN(info->channel); +		__entry->ret = ret; +		__entry->channel_time = info->channel_time; +		__entry->channel_time_busy = info->channel_time_busy; +		__entry->channel_time_ext_busy = info->channel_time_ext_busy; +		__entry->channel_time_rx = info->channel_time_rx; +		__entry->channel_time_tx = info->channel_time_tx; +		__entry->filled = info->filled; +		__entry->noise = info->noise; +	), +	TP_printk(WIPHY_PR_FMT ", returned: %d, " CHAN_PR_FMT +		  ", channel time: %llu, channel time busy: %llu, " +		  "channel time extension busy: %llu, channel time rx: %llu, " +		  "channel time tx: %llu, filled: %u, noise: %d", +		  WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG, +		  __entry->channel_time, __entry->channel_time_busy, +		  __entry->channel_time_ext_busy, __entry->channel_time_rx, +		  __entry->channel_time_tx, __entry->filled, __entry->noise) +); + +TRACE_EVENT(rdev_tdls_oper, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 u8 *peer, enum nl80211_tdls_operation oper), +	TP_ARGS(wiphy, netdev, peer, oper), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(peer) +		__field(enum nl80211_tdls_operation, oper) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(peer, peer); +		__entry->oper = oper; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", oper: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper) +); + +DECLARE_EVENT_CLASS(rdev_pmksa, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_pmksa *pmksa), +	TP_ARGS(wiphy, netdev, pmksa), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(bssid, pmksa->bssid); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid)) +); + +TRACE_EVENT(rdev_probe_client, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 const u8 *peer), +	TP_ARGS(wiphy, netdev, peer), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(peer) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(peer, peer); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer)) +); + +DEFINE_EVENT(rdev_pmksa, rdev_set_pmksa, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_pmksa *pmksa), +	TP_ARGS(wiphy, netdev, pmksa) +); + +DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_pmksa *pmksa), +	TP_ARGS(wiphy, netdev, pmksa) +); + +TRACE_EVENT(rdev_remain_on_channel, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, +		 struct ieee80211_channel *chan, +		 unsigned int duration), +	TP_ARGS(wiphy, wdev, chan, duration), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +		CHAN_ENTRY +		__field(unsigned int, duration) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WDEV_ASSIGN; +		CHAN_ASSIGN(chan); +		__entry->duration = duration; +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u", +		  WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration) +); + +TRACE_EVENT(rdev_return_int_cookie, +	TP_PROTO(struct wiphy *wiphy, int ret, u64 cookie), +	TP_ARGS(wiphy, ret, cookie), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, ret) +		__field(u64, cookie) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->ret = ret; +		__entry->cookie = cookie; +	), +	TP_printk(WIPHY_PR_FMT ", returned %d, cookie: %llu", +		  WIPHY_PR_ARG, __entry->ret, __entry->cookie) +); + +TRACE_EVENT(rdev_cancel_remain_on_channel, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie), +	TP_ARGS(wiphy, wdev, cookie), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +		__field(u64, cookie) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WDEV_ASSIGN; +		__entry->cookie = cookie; +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu", +		  WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) +); + +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), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +		CHAN_ENTRY +		__field(bool, offchan) +		__field(unsigned int, wait) +		__field(bool, no_cck) +		__field(bool, dont_wait_for_ack) +	), +	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; +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s," +		  " wait: %u, no cck: %s, dont wait for ack: %s", +		  WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, +		  BOOL_TO_STR(__entry->offchan), __entry->wait, +		  BOOL_TO_STR(__entry->no_cck), +		  BOOL_TO_STR(__entry->dont_wait_for_ack)) +); + +TRACE_EVENT(rdev_set_noack_map, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 u16 noack_map), +	TP_ARGS(wiphy, netdev, noack_map), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(u16, noack_map) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->noack_map = noack_map; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", noack_map: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map) +); + +TRACE_EVENT(rdev_get_et_sset_count, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset), +	TP_ARGS(wiphy, netdev, sset), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(int, sset) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->sset = sset; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) +); + +TRACE_EVENT(rdev_get_et_strings, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset), +	TP_ARGS(wiphy, netdev, sset), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(u32, sset) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		__entry->sset = sset; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), +	TP_ARGS(wiphy, wdev) +); + +TRACE_EVENT(rdev_return_chandef, +	TP_PROTO(struct wiphy *wiphy, int ret, +		 struct cfg80211_chan_def *chandef), +	TP_ARGS(wiphy, ret, chandef), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, ret) +		CHAN_DEF_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		if (ret == 0) +			CHAN_DEF_ASSIGN(chandef); +		else +			CHAN_DEF_ASSIGN((struct cfg80211_chan_def *)NULL); +		__entry->ret = ret; +	), +	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", ret: %d", +		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->ret) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_start_p2p_device, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), +	TP_ARGS(wiphy, wdev) +); + +DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), +	TP_ARGS(wiphy, wdev) +); + +TRACE_EVENT(rdev_set_mac_acl, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_acl_data *params), +	TP_ARGS(wiphy, netdev, params), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		__field(u32, acl_policy) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WIPHY_ASSIGN; +		__entry->acl_policy = params->acl_policy; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy) +); + +/************************************************************* + *	     cfg80211 exported functions traces		     * + *************************************************************/ + +TRACE_EVENT(cfg80211_return_bool, +	TP_PROTO(bool ret), +	TP_ARGS(ret), +	TP_STRUCT__entry( +		__field(bool, ret) +	), +	TP_fast_assign( +		__entry->ret = ret; +	), +	TP_printk("returned %s", BOOL_TO_STR(__entry->ret)) +); + +DECLARE_EVENT_CLASS(cfg80211_netdev_mac_evt, +	TP_PROTO(struct net_device *netdev, const u8 *macaddr), +	TP_ARGS(netdev, macaddr), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(macaddr) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(macaddr, macaddr); +	), +	TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT, +		  NETDEV_PR_ARG, MAC_PR_ARG(macaddr)) +); + +DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_notify_new_peer_candidate, +	TP_PROTO(struct net_device *netdev, const u8 *macaddr), +	TP_ARGS(netdev, macaddr) +); + +DECLARE_EVENT_CLASS(netdev_evt_only, +	TP_PROTO(struct net_device *netdev), +	TP_ARGS(netdev), +	TP_STRUCT__entry( +		NETDEV_ENTRY +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +	), +	TP_printk(NETDEV_PR_FMT , NETDEV_PR_ARG) +); + +DEFINE_EVENT(netdev_evt_only, cfg80211_send_rx_auth, +	TP_PROTO(struct net_device *netdev), +	TP_ARGS(netdev) +); + +TRACE_EVENT(cfg80211_send_rx_assoc, +	TP_PROTO(struct net_device *netdev, struct cfg80211_bss *bss), +	TP_ARGS(netdev, bss), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		CHAN_ENTRY +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(bssid, bss->bssid); +		CHAN_ASSIGN(bss->channel); +	), +	TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", " CHAN_PR_FMT, +		  NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG) +); + +DEFINE_EVENT(netdev_evt_only, __cfg80211_send_deauth, +	TP_PROTO(struct net_device *netdev), +	TP_ARGS(netdev) +); + +DEFINE_EVENT(netdev_evt_only, __cfg80211_send_disassoc, +	TP_PROTO(struct net_device *netdev), +	TP_ARGS(netdev) +); + +DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_deauth, +	TP_PROTO(struct net_device *netdev), +	TP_ARGS(netdev) +); + +DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_disassoc, +	TP_PROTO(struct net_device *netdev), +	TP_ARGS(netdev) +); + +DECLARE_EVENT_CLASS(netdev_mac_evt, +	TP_PROTO(struct net_device *netdev, const u8 *mac), +	TP_ARGS(netdev, mac), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(mac) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(mac, mac) +	), +	TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT, +		  NETDEV_PR_ARG, MAC_PR_ARG(mac)) +); + +DEFINE_EVENT(netdev_mac_evt, cfg80211_send_auth_timeout, +	TP_PROTO(struct net_device *netdev, const u8 *mac), +	TP_ARGS(netdev, mac) +); + +DEFINE_EVENT(netdev_mac_evt, cfg80211_send_assoc_timeout, +	TP_PROTO(struct net_device *netdev, const u8 *mac), +	TP_ARGS(netdev, mac) +); + +TRACE_EVENT(cfg80211_michael_mic_failure, +	TP_PROTO(struct net_device *netdev, const u8 *addr, +		 enum nl80211_key_type key_type, int key_id, const u8 *tsc), +	TP_ARGS(netdev, addr, key_type, key_id, tsc), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(addr) +		__field(enum nl80211_key_type, key_type) +		__field(int, key_id) +		__array(u8, tsc, 6) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(addr, addr); +		__entry->key_type = key_type; +		__entry->key_id = key_id; +		memcpy(__entry->tsc, tsc, 6); +	), +	TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm", +		  NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->key_type, +		  __entry->key_id, __entry->tsc) +); + +TRACE_EVENT(cfg80211_ready_on_channel, +	TP_PROTO(struct wireless_dev *wdev, u64 cookie, +		 struct ieee80211_channel *chan, +		 unsigned int duration), +	TP_ARGS(wdev, cookie, chan, duration), +	TP_STRUCT__entry( +		WDEV_ENTRY +		__field(u64, cookie) +		CHAN_ENTRY +		__field(unsigned int, duration) +	), +	TP_fast_assign( +		WDEV_ASSIGN; +		__entry->cookie = cookie; +		CHAN_ASSIGN(chan); +		__entry->duration = duration; +	), +	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", duration: %u", +		  WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG, +		  __entry->duration) +); + +TRACE_EVENT(cfg80211_ready_on_channel_expired, +	TP_PROTO(struct wireless_dev *wdev, u64 cookie, +		 struct ieee80211_channel *chan), +	TP_ARGS(wdev, cookie, chan), +	TP_STRUCT__entry( +		WDEV_ENTRY +		__field(u64, cookie) +		CHAN_ENTRY +	), +	TP_fast_assign( +		WDEV_ASSIGN; +		__entry->cookie = cookie; +		CHAN_ASSIGN(chan); +	), +	TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT, +		  WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG) +); + +TRACE_EVENT(cfg80211_new_sta, +	TP_PROTO(struct net_device *netdev, const u8 *mac_addr, +		 struct station_info *sinfo), +	TP_ARGS(netdev, mac_addr, sinfo), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(mac_addr) +		SINFO_ENTRY +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(mac_addr, mac_addr); +		SINFO_ASSIGN; +	), +	TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, +		  NETDEV_PR_ARG, MAC_PR_ARG(mac_addr)) +); + +DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_del_sta, +	TP_PROTO(struct net_device *netdev, const u8 *macaddr), +	TP_ARGS(netdev, macaddr) +); + +TRACE_EVENT(cfg80211_rx_mgmt, +	TP_PROTO(struct wireless_dev *wdev, int freq, int sig_mbm), +	TP_ARGS(wdev, freq, sig_mbm), +	TP_STRUCT__entry( +		WDEV_ENTRY +		__field(int, freq) +		__field(int, sig_mbm) +	), +	TP_fast_assign( +		WDEV_ASSIGN; +		__entry->freq = freq; +		__entry->sig_mbm = sig_mbm; +	), +	TP_printk(WDEV_PR_FMT ", freq: %d, sig mbm: %d", +		  WDEV_PR_ARG, __entry->freq, __entry->sig_mbm) +); + +TRACE_EVENT(cfg80211_mgmt_tx_status, +	TP_PROTO(struct wireless_dev *wdev, u64 cookie, bool ack), +	TP_ARGS(wdev, cookie, ack), +	TP_STRUCT__entry( +		WDEV_ENTRY +		__field(u64, cookie) +		__field(bool, ack) +	), +	TP_fast_assign( +		WDEV_ASSIGN; +		__entry->cookie = cookie; +		__entry->ack = ack; +	), +	TP_printk(WDEV_PR_FMT", cookie: %llu, ack: %s", +		  WDEV_PR_ARG, __entry->cookie, BOOL_TO_STR(__entry->ack)) +); + +TRACE_EVENT(cfg80211_cqm_rssi_notify, +	TP_PROTO(struct net_device *netdev, +		 enum nl80211_cqm_rssi_threshold_event rssi_event), +	TP_ARGS(netdev, rssi_event), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		__field(enum nl80211_cqm_rssi_threshold_event, rssi_event) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		__entry->rssi_event = rssi_event; +	), +	TP_printk(NETDEV_PR_FMT ", rssi event: %d", +		  NETDEV_PR_ARG, __entry->rssi_event) +); + +TRACE_EVENT(cfg80211_reg_can_beacon, +	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), +	TP_ARGS(wiphy, chandef), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		CHAN_DEF_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		CHAN_DEF_ASSIGN(chandef); +	), +	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, +		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); + +TRACE_EVENT(cfg80211_chandef_dfs_required, +	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), +	TP_ARGS(wiphy, chandef), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		CHAN_DEF_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		CHAN_DEF_ASSIGN(chandef); +	), +	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, +		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); + +TRACE_EVENT(cfg80211_ch_switch_notify, +	TP_PROTO(struct net_device *netdev, +		 struct cfg80211_chan_def *chandef), +	TP_ARGS(netdev, chandef), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		CHAN_DEF_ENTRY +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		CHAN_DEF_ASSIGN(chandef); +	), +	TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT, +		  NETDEV_PR_ARG, CHAN_DEF_PR_ARG) +); + +TRACE_EVENT(cfg80211_radar_event, +	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), +	TP_ARGS(wiphy, chandef), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		CHAN_DEF_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		CHAN_DEF_ASSIGN(chandef); +	), +	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, +		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); + +TRACE_EVENT(cfg80211_cac_event, +	TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt), +	TP_ARGS(netdev, evt), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		__field(enum nl80211_radar_event, evt) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		__entry->evt = evt; +	), +	TP_printk(NETDEV_PR_FMT ",  event: %d", +		  NETDEV_PR_ARG, __entry->evt) +); + +DECLARE_EVENT_CLASS(cfg80211_rx_evt, +	TP_PROTO(struct net_device *netdev, const u8 *addr), +	TP_ARGS(netdev, addr), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(addr) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(addr, addr); +	), +	TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr)) +); + +DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined, +	TP_PROTO(struct net_device *netdev, const u8 *addr), +	TP_ARGS(netdev, addr) +); + +DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame, +	TP_PROTO(struct net_device *netdev, const u8 *addr), +	TP_ARGS(netdev, addr) +); + +DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame, +	TP_PROTO(struct net_device *netdev, const u8 *addr), +	TP_ARGS(netdev, addr) +); + +TRACE_EVENT(cfg80211_probe_status, +	TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie, +		 bool acked), +	TP_ARGS(netdev, addr, cookie, acked), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(addr) +		__field(u64, cookie) +		__field(bool, acked) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(addr, addr); +		__entry->cookie = cookie; +		__entry->acked = acked; +	), +	TP_printk(NETDEV_PR_FMT " addr:" MAC_PR_FMT ", cookie: %llu, acked: %s", +		  NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->cookie, +		  BOOL_TO_STR(__entry->acked)) +); + +TRACE_EVENT(cfg80211_cqm_pktloss_notify, +	TP_PROTO(struct net_device *netdev, const u8 *peer, u32 num_packets), +	TP_ARGS(netdev, peer, num_packets), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(peer) +		__field(u32, num_packets) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(peer, peer); +		__entry->num_packets = num_packets; +	), +	TP_printk(NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", num of lost packets: %u", +		  NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->num_packets) +); + +DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_gtk_rekey_notify, +	TP_PROTO(struct net_device *netdev, const u8 *macaddr), +	TP_ARGS(netdev, macaddr) +); + +TRACE_EVENT(cfg80211_pmksa_candidate_notify, +	TP_PROTO(struct net_device *netdev, int index, const u8 *bssid, +		 bool preauth), +	TP_ARGS(netdev, index, bssid, preauth), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		__field(int, index) +		MAC_ENTRY(bssid) +		__field(bool, preauth) +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		__entry->index = index; +		MAC_ASSIGN(bssid, bssid); +		__entry->preauth = preauth; +	), +	TP_printk(NETDEV_PR_FMT ", index:%d, bssid: " MAC_PR_FMT ", pre auth: %s", +		  NETDEV_PR_ARG, __entry->index, MAC_PR_ARG(bssid), +		  BOOL_TO_STR(__entry->preauth)) +); + +TRACE_EVENT(cfg80211_report_obss_beacon, +	TP_PROTO(struct wiphy *wiphy, const u8 *frame, size_t len, +		 int freq, int sig_dbm), +	TP_ARGS(wiphy, frame, len, freq, sig_dbm), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		__field(int, freq) +		__field(int, sig_dbm) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		__entry->freq = freq; +		__entry->sig_dbm = sig_dbm; +	), +	TP_printk(WIPHY_PR_FMT ", freq: %d, sig_dbm: %d", +		  WIPHY_PR_ARG, __entry->freq, __entry->sig_dbm) +); + +TRACE_EVENT(cfg80211_tdls_oper_request, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *peer, +		 enum nl80211_tdls_operation oper, u16 reason_code), +	TP_ARGS(wiphy, netdev, peer, oper, reason_code), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		MAC_ENTRY(peer) +		__field(enum nl80211_tdls_operation, oper) +		__field(u16, reason_code) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		MAC_ASSIGN(peer, peer); +		__entry->oper = oper; +		__entry->reason_code = reason_code; +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", oper: %d, reason_code %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper, +		  __entry->reason_code) +	); + +TRACE_EVENT(cfg80211_scan_done, +	TP_PROTO(struct cfg80211_scan_request *request, bool aborted), +	TP_ARGS(request, aborted), +	TP_STRUCT__entry( +		__field(u32, n_channels) +		__dynamic_array(u8, ie, request ? request->ie_len : 0) +		__array(u32, rates, IEEE80211_NUM_BANDS) +		__field(u32, wdev_id) +		MAC_ENTRY(wiphy_mac) +		__field(bool, no_cck) +		__field(bool, aborted) +	), +	TP_fast_assign( +		if (request) { +			memcpy(__get_dynamic_array(ie), request->ie, +			       request->ie_len); +			memcpy(__entry->rates, request->rates, +			       IEEE80211_NUM_BANDS); +			__entry->wdev_id = request->wdev ? +					request->wdev->identifier : 0; +			if (request->wiphy) +				MAC_ASSIGN(wiphy_mac, +					   request->wiphy->perm_addr); +			__entry->no_cck = request->no_cck; +		} +		__entry->aborted = aborted; +	), +	TP_printk("aborted: %s", BOOL_TO_STR(__entry->aborted)) +); + +DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_results, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy) +); + +DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_stopped, +	TP_PROTO(struct wiphy *wiphy), +	TP_ARGS(wiphy) +); + +TRACE_EVENT(cfg80211_get_bss, +	TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, +		 const u8 *bssid, const u8 *ssid, size_t ssid_len, +		 u16 capa_mask, u16 capa_val), +	TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, capa_mask, capa_val), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		CHAN_ENTRY +		MAC_ENTRY(bssid) +		__dynamic_array(u8, ssid, ssid_len) +		__field(u16, capa_mask) +		__field(u16, capa_val) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		CHAN_ASSIGN(channel); +		MAC_ASSIGN(bssid, bssid); +		memcpy(__get_dynamic_array(ssid), ssid, ssid_len); +		__entry->capa_mask = capa_mask; +		__entry->capa_val = capa_val; +	), +	TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT ", buf: %#.2x, " +		  "capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG, +		  MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0], +		  __entry->capa_mask, __entry->capa_val) +); + +TRACE_EVENT(cfg80211_inform_bss_frame, +	TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, +		 struct ieee80211_mgmt *mgmt, size_t len, +		 s32 signal), +	TP_ARGS(wiphy, channel, mgmt, len, signal), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		CHAN_ENTRY +		__dynamic_array(u8, mgmt, len) +		__field(s32, signal) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		CHAN_ASSIGN(channel); +		if (mgmt) +			memcpy(__get_dynamic_array(mgmt), mgmt, len); +		__entry->signal = signal; +	), +	TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d", +		  WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal) +); + +DECLARE_EVENT_CLASS(cfg80211_bss_evt, +	TP_PROTO(struct cfg80211_bss *pub), +	TP_ARGS(pub), +	TP_STRUCT__entry( +		MAC_ENTRY(bssid) +		CHAN_ENTRY +	), +	TP_fast_assign( +		MAC_ASSIGN(bssid, pub->bssid); +		CHAN_ASSIGN(pub->channel); +	), +	TP_printk(MAC_PR_FMT ", " CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG) +); + +DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss, +	TP_PROTO(struct cfg80211_bss *pub), +	TP_ARGS(pub) +); + +TRACE_EVENT(cfg80211_return_uint, +	TP_PROTO(unsigned int ret), +	TP_ARGS(ret), +	TP_STRUCT__entry( +		__field(unsigned int, ret) +	), +	TP_fast_assign( +		__entry->ret = ret; +	), +	TP_printk("ret: %d", __entry->ret) +); + +TRACE_EVENT(cfg80211_return_u32, +	TP_PROTO(u32 ret), +	TP_ARGS(ret), +	TP_STRUCT__entry( +		__field(u32, ret) +	), +	TP_fast_assign( +		__entry->ret = ret; +	), +	TP_printk("ret: %u", __entry->ret) +); + +TRACE_EVENT(cfg80211_report_wowlan_wakeup, +	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, +		 struct cfg80211_wowlan_wakeup *wakeup), +	TP_ARGS(wiphy, wdev, wakeup), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		WDEV_ENTRY +		__field(bool, disconnect) +		__field(bool, magic_pkt) +		__field(bool, gtk_rekey_failure) +		__field(bool, eap_identity_req) +		__field(bool, four_way_handshake) +		__field(bool, rfkill_release) +		__field(s32, pattern_idx) +		__field(u32, packet_len) +		__dynamic_array(u8, packet, wakeup->packet_present_len) +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		WDEV_ASSIGN; +		__entry->disconnect = wakeup->disconnect; +		__entry->magic_pkt = wakeup->magic_pkt; +		__entry->gtk_rekey_failure = wakeup->gtk_rekey_failure; +		__entry->eap_identity_req = wakeup->eap_identity_req; +		__entry->four_way_handshake = wakeup->four_way_handshake; +		__entry->rfkill_release = wakeup->rfkill_release; +		__entry->pattern_idx = wakeup->pattern_idx; +		__entry->packet_len = wakeup->packet_len; +		if (wakeup->packet && wakeup->packet_present_len) +			memcpy(__get_dynamic_array(packet), wakeup->packet, +			       wakeup->packet_present_len); +	), +	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) +); + +#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace +#include <trace/define_trace.h> diff --git a/net/wireless/util.c b/net/wireless/util.c index 2762e832998..37a56ee1e1e 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -11,6 +11,8 @@  #include <net/ip.h>  #include <net/dsfield.h>  #include "core.h" +#include "rdev-ops.h" +  struct ieee80211_rate *  ieee80211_get_response_rate(struct ieee80211_supported_band *sband, @@ -686,10 +688,13 @@ EXPORT_SYMBOL(cfg80211_classify8021d);  const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie)  { -	if (bss->information_elements == NULL) +	const struct cfg80211_bss_ies *ies; + +	ies = rcu_dereference(bss->ies); +	if (!ies)  		return NULL; -	return cfg80211_find_ie(ie, bss->information_elements, -				 bss->len_information_elements); + +	return cfg80211_find_ie(ie, ies->data, ies->len);  }  EXPORT_SYMBOL(ieee80211_bss_get_ie); @@ -705,19 +710,18 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)  	for (i = 0; i < 6; i++) {  		if (!wdev->connect_keys->params[i].cipher)  			continue; -		if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL, -					&wdev->connect_keys->params[i])) { +		if (rdev_add_key(rdev, dev, i, false, NULL, +				 &wdev->connect_keys->params[i])) {  			netdev_err(dev, "failed to set key %d\n", i);  			continue;  		}  		if (wdev->connect_keys->def == i) -			if (rdev->ops->set_default_key(wdev->wiphy, dev, -						       i, true, true)) { +			if (rdev_set_default_key(rdev, dev, i, true, true)) {  				netdev_err(dev, "failed to set defkey %d\n", i);  				continue;  			}  		if (wdev->connect_keys->defmgmt == i) -			if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) +			if (rdev_set_default_mgmt_key(rdev, dev, i))  				netdev_err(dev, "failed to set mgtdef %d\n", i);  	} @@ -850,8 +854,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,  		cfg80211_process_rdev_events(rdev);  	} -	err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, -					     ntype, flags, params); +	err = rdev_change_virtual_intf(rdev, dev, ntype, flags, params);  	WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); @@ -944,14 +947,86 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)  	return __mcs2bitrate[rate->mcs];  } +static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate) +{ +	static const u32 base[4][10] = { +		{   6500000, +		   13000000, +		   19500000, +		   26000000, +		   39000000, +		   52000000, +		   58500000, +		   65000000, +		   78000000, +		   0, +		}, +		{  13500000, +		   27000000, +		   40500000, +		   54000000, +		   81000000, +		  108000000, +		  121500000, +		  135000000, +		  162000000, +		  180000000, +		}, +		{  29300000, +		   58500000, +		   87800000, +		  117000000, +		  175500000, +		  234000000, +		  263300000, +		  292500000, +		  351000000, +		  390000000, +		}, +		{  58500000, +		  117000000, +		  175500000, +		  234000000, +		  351000000, +		  468000000, +		  526500000, +		  585000000, +		  702000000, +		  780000000, +		}, +	}; +	u32 bitrate; +	int idx; + +	if (WARN_ON_ONCE(rate->mcs > 9)) +		return 0; + +	idx = rate->flags & (RATE_INFO_FLAGS_160_MHZ_WIDTH | +			     RATE_INFO_FLAGS_80P80_MHZ_WIDTH) ? 3 : +		  rate->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH ? 2 : +		  rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH ? 1 : 0; + +	bitrate = base[idx][rate->mcs]; +	bitrate *= rate->nss; + +	if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) +		bitrate = (bitrate / 9) * 10; + +	/* do NOT round down here */ +	return (bitrate + 50000) / 100000; +} +  u32 cfg80211_calculate_bitrate(struct rate_info *rate)  {  	int modulation, streams, bitrate; -	if (!(rate->flags & RATE_INFO_FLAGS_MCS)) +	if (!(rate->flags & RATE_INFO_FLAGS_MCS) && +	    !(rate->flags & RATE_INFO_FLAGS_VHT_MCS))  		return rate->legacy;  	if (rate->flags & RATE_INFO_FLAGS_60G)  		return cfg80211_calculate_bitrate_60g(rate); +	if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) +		return cfg80211_calculate_bitrate_vht(rate);  	/* the formula below does only work for MCS values smaller than 32 */  	if (WARN_ON_ONCE(rate->mcs >= 32)) @@ -980,6 +1055,106 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)  }  EXPORT_SYMBOL(cfg80211_calculate_bitrate); +int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, +			  enum ieee80211_p2p_attr_id attr, +			  u8 *buf, unsigned int bufsize) +{ +	u8 *out = buf; +	u16 attr_remaining = 0; +	bool desired_attr = false; +	u16 desired_len = 0; + +	while (len > 0) { +		unsigned int iedatalen; +		unsigned int copy; +		const u8 *iedata; + +		if (len < 2) +			return -EILSEQ; +		iedatalen = ies[1]; +		if (iedatalen + 2 > len) +			return -EILSEQ; + +		if (ies[0] != WLAN_EID_VENDOR_SPECIFIC) +			goto cont; + +		if (iedatalen < 4) +			goto cont; + +		iedata = ies + 2; + +		/* check WFA OUI, P2P subtype */ +		if (iedata[0] != 0x50 || iedata[1] != 0x6f || +		    iedata[2] != 0x9a || iedata[3] != 0x09) +			goto cont; + +		iedatalen -= 4; +		iedata += 4; + +		/* check attribute continuation into this IE */ +		copy = min_t(unsigned int, attr_remaining, iedatalen); +		if (copy && desired_attr) { +			desired_len += copy; +			if (out) { +				memcpy(out, iedata, min(bufsize, copy)); +				out += min(bufsize, copy); +				bufsize -= min(bufsize, copy); +			} + + +			if (copy == attr_remaining) +				return desired_len; +		} + +		attr_remaining -= copy; +		if (attr_remaining) +			goto cont; + +		iedatalen -= copy; +		iedata += copy; + +		while (iedatalen > 0) { +			u16 attr_len; + +			/* P2P attribute ID & size must fit */ +			if (iedatalen < 3) +				return -EILSEQ; +			desired_attr = iedata[0] == attr; +			attr_len = get_unaligned_le16(iedata + 1); +			iedatalen -= 3; +			iedata += 3; + +			copy = min_t(unsigned int, attr_len, iedatalen); + +			if (desired_attr) { +				desired_len += copy; +				if (out) { +					memcpy(out, iedata, min(bufsize, copy)); +					out += min(bufsize, copy); +					bufsize -= min(bufsize, copy); +				} + +				if (copy == attr_len) +					return desired_len; +			} + +			iedata += copy; +			iedatalen -= copy; +			attr_remaining = attr_len - copy; +		} + + cont: +		len -= ies[1] + 2; +		ies += ies[1] + 2; +	} + +	if (attr_remaining && desired_attr) +		return -EILSEQ; + +	return -ENOENT; +} +EXPORT_SYMBOL(cfg80211_get_p2p_attr); +  int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,  				 u32 beacon_int)  { @@ -1009,7 +1184,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  				 struct wireless_dev *wdev,  				 enum nl80211_iftype iftype,  				 struct ieee80211_channel *chan, -				 enum cfg80211_chan_mode chanmode) +				 enum cfg80211_chan_mode chanmode, +				 u8 radar_detect)  {  	struct wireless_dev *wdev_iter;  	u32 used_iftypes = BIT(iftype); @@ -1020,14 +1196,46 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  	enum cfg80211_chan_mode chmode;  	int num_different_channels = 0;  	int total = 1; +	bool radar_required;  	int i, j;  	ASSERT_RTNL();  	lockdep_assert_held(&rdev->devlist_mtx); +	if (WARN_ON(hweight32(radar_detect) > 1)) +		return -EINVAL; + +	switch (iftype) { +	case NL80211_IFTYPE_ADHOC: +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_AP_VLAN: +	case NL80211_IFTYPE_MESH_POINT: +	case NL80211_IFTYPE_P2P_GO: +	case NL80211_IFTYPE_WDS: +		radar_required = !!(chan && +				    (chan->flags & IEEE80211_CHAN_RADAR)); +		break; +	case NL80211_IFTYPE_P2P_CLIENT: +	case NL80211_IFTYPE_STATION: +	case NL80211_IFTYPE_P2P_DEVICE: +	case NL80211_IFTYPE_MONITOR: +		radar_required = false; +		break; +	case NUM_NL80211_IFTYPES: +	case NL80211_IFTYPE_UNSPECIFIED: +	default: +		return -EINVAL; +	} + +	if (radar_required && !radar_detect) +		return -EINVAL; +  	/* Always allow software iftypes */ -	if (rdev->wiphy.software_iftypes & BIT(iftype)) +	if (rdev->wiphy.software_iftypes & BIT(iftype)) { +		if (radar_detect) +			return -EINVAL;  		return 0; +	}  	memset(num, 0, sizeof(num));  	memset(used_channels, 0, sizeof(used_channels)); @@ -1100,7 +1308,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  		used_iftypes |= BIT(wdev_iter->iftype);  	} -	if (total == 1) +	if (total == 1 && !radar_detect)  		return 0;  	for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { @@ -1133,6 +1341,9 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  			}  		} +		if (radar_detect && !(c->radar_detect_widths & radar_detect)) +			goto cont; +  		/*  		 * Finally check that all iftypes that we're currently  		 * using are actually part of this combination. If they diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 494379eb464..d997d0f0c54 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -19,6 +19,7 @@  #include <net/cfg80211-wext.h>  #include "wext-compat.h"  #include "core.h" +#include "rdev-ops.h"  int cfg80211_wext_giwname(struct net_device *dev,  			  struct iw_request_info *info, @@ -175,7 +176,7 @@ int cfg80211_wext_giwrange(struct net_device *dev,  	case CFG80211_SIGNAL_TYPE_NONE:  		break;  	case CFG80211_SIGNAL_TYPE_MBM: -		range->max_qual.level = -110; +		range->max_qual.level = (u8)-110;  		range->max_qual.qual = 70;  		range->avg_qual.qual = 35;  		range->max_qual.updated |= IW_QUAL_DBM; @@ -301,8 +302,7 @@ int cfg80211_wext_siwrts(struct net_device *dev,  	else  		wdev->wiphy->rts_threshold = rts->value; -	err = rdev->ops->set_wiphy_params(wdev->wiphy, -					  WIPHY_PARAM_RTS_THRESHOLD); +	err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_RTS_THRESHOLD);  	if (err)  		wdev->wiphy->rts_threshold = orts; @@ -342,8 +342,7 @@ int cfg80211_wext_siwfrag(struct net_device *dev,  		wdev->wiphy->frag_threshold = frag->value & ~0x1;  	} -	err = rdev->ops->set_wiphy_params(wdev->wiphy, -					  WIPHY_PARAM_FRAG_THRESHOLD); +	err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_FRAG_THRESHOLD);  	if (err)  		wdev->wiphy->frag_threshold = ofrag; @@ -396,7 +395,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,  	if (!changed)  		return 0; -	err = rdev->ops->set_wiphy_params(wdev->wiphy, changed); +	err = rdev_set_wiphy_params(rdev, changed);  	if (err) {  		wdev->wiphy->retry_short = oshort;  		wdev->wiphy->retry_long = olong; @@ -490,8 +489,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,  			    !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))  				err = -ENOENT;  			else -				err = rdev->ops->del_key(&rdev->wiphy, dev, idx, -							 pairwise, addr); +				err = rdev_del_key(rdev, dev, idx, pairwise, +						   addr);  		}  		wdev->wext.connect.privacy = false;  		/* @@ -525,8 +524,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,  	err = 0;  	if (wdev->current_bss) -		err = rdev->ops->add_key(&rdev->wiphy, dev, idx, -					 pairwise, addr, params); +		err = rdev_add_key(rdev, dev, idx, pairwise, addr, params);  	if (err)  		return err; @@ -552,8 +550,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,  				__cfg80211_leave_ibss(rdev, wdev->netdev, true);  				rejoin = true;  			} -			err = rdev->ops->set_default_key(&rdev->wiphy, dev, -							 idx, true, true); +			err = rdev_set_default_key(rdev, dev, idx, true, true);  		}  		if (!err) {  			wdev->wext.default_key = idx; @@ -566,8 +563,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,  	if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC &&  	    (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) {  		if (wdev->current_bss) -			err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, -							      dev, idx); +			err = rdev_set_default_mgmt_key(rdev, dev, idx);  		if (!err)  			wdev->wext.default_mgmt_key = idx;  		return err; @@ -631,8 +627,8 @@ static int cfg80211_wext_siwencode(struct net_device *dev,  		err = 0;  		wdev_lock(wdev);  		if (wdev->current_bss) -			err = rdev->ops->set_default_key(&rdev->wiphy, dev, -							 idx, true, true); +			err = rdev_set_default_key(rdev, dev, idx, true, +						   true);  		if (!err)  			wdev->wext.default_key = idx;  		wdev_unlock(wdev); @@ -788,6 +784,9 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_chan_def chandef = { +		.width = NL80211_CHAN_WIDTH_20_NOHT, +	};  	int freq, err;  	switch (wdev->iftype) { @@ -801,8 +800,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,  			return freq;  		if (freq == 0)  			return -EINVAL; +		chandef.center_freq1 = freq; +		chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); +		if (!chandef.chan) +			return -EINVAL;  		mutex_lock(&rdev->devlist_mtx); -		err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); +		err = cfg80211_set_monitor_channel(rdev, &chandef);  		mutex_unlock(&rdev->devlist_mtx);  		return err;  	case NL80211_IFTYPE_MESH_POINT: @@ -811,9 +814,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,  			return freq;  		if (freq == 0)  			return -EINVAL; +		chandef.center_freq1 = freq; +		chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); +		if (!chandef.chan) +			return -EINVAL;  		mutex_lock(&rdev->devlist_mtx); -		err = cfg80211_set_mesh_freq(rdev, wdev, freq, -					     NL80211_CHAN_NO_HT); +		err = cfg80211_set_mesh_channel(rdev, wdev, &chandef);  		mutex_unlock(&rdev->devlist_mtx);  		return err;  	default: @@ -827,8 +833,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); -	struct ieee80211_channel *chan; -	enum nl80211_channel_type channel_type; +	struct cfg80211_chan_def chandef; +	int ret;  	switch (wdev->iftype) {  	case NL80211_IFTYPE_STATION: @@ -839,10 +845,10 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,  		if (!rdev->ops->get_channel)  			return -EINVAL; -		chan = rdev->ops->get_channel(wdev->wiphy, wdev, &channel_type); -		if (!chan) -			return -EINVAL; -		freq->m = chan->center_freq; +		ret = rdev_get_channel(rdev, wdev, &chandef); +		if (ret) +			return ret; +		freq->m = chandef.chan->center_freq;  		freq->e = 6;  		return 0;  	default: @@ -899,7 +905,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,  		return 0;  	} -	return rdev->ops->set_tx_power(wdev->wiphy, type, DBM_TO_MBM(dbm)); +	return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));  }  static int cfg80211_wext_giwtxpower(struct net_device *dev, @@ -918,7 +924,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,  	if (!rdev->ops->get_tx_power)  		return -EOPNOTSUPP; -	err = rdev->ops->get_tx_power(wdev->wiphy, &val); +	err = rdev_get_tx_power(rdev, wdev, &val);  	if (err)  		return err; @@ -1158,7 +1164,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,  			timeout = wrq->value / 1000;  	} -	err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); +	err = rdev_set_power_mgmt(rdev, dev, ps, timeout);  	if (err)  		return err; @@ -1200,7 +1206,7 @@ static int cfg80211_wds_wext_siwap(struct net_device *dev,  	if (!rdev->ops->set_wds_peer)  		return -EOPNOTSUPP; -	err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); +	err = rdev_set_wds_peer(rdev, dev, (u8 *)&addr->sa_data);  	if (err)  		return err; @@ -1272,7 +1278,7 @@ static int cfg80211_wext_siwrate(struct net_device *dev,  	if (!match)  		return -EINVAL; -	return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); +	return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);  }  static int cfg80211_wext_giwrate(struct net_device *dev, @@ -1302,7 +1308,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,  	if (err)  		return err; -	err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); +	err = rdev_get_station(rdev, dev, addr, &sinfo);  	if (err)  		return err; @@ -1339,7 +1345,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)  	memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);  	wdev_unlock(wdev); -	if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo)) +	if (rdev_get_station(rdev, dev, bssid, &sinfo))  		return NULL;  	memset(&wstats, 0, sizeof(wstats)); @@ -1474,19 +1480,19 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev,  		if (!rdev->ops->set_pmksa)  			return -EOPNOTSUPP; -		return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa); +		return rdev_set_pmksa(rdev, dev, &cfg_pmksa);  	case IW_PMKSA_REMOVE:  		if (!rdev->ops->del_pmksa)  			return -EOPNOTSUPP; -		return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa); +		return rdev_del_pmksa(rdev, dev, &cfg_pmksa);  	case IW_PMKSA_FLUSH:  		if (!rdev->ops->flush_pmksa)  			return -EOPNOTSUPP; -		return rdev->ops->flush_pmksa(&rdev->wiphy, dev); +		return rdev_flush_pmksa(rdev, dev);  	default:  		return -EOPNOTSUPP; diff --git a/net/wireless/wext-proc.c b/net/wireless/wext-proc.c index 8bafa31fa9f..e98a01c1034 100644 --- a/net/wireless/wext-proc.c +++ b/net/wireless/wext-proc.c @@ -143,7 +143,8 @@ static const struct file_operations wireless_seq_fops = {  int __net_init wext_proc_init(struct net *net)  {  	/* Create /proc/net/wireless entry */ -	if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) +	if (!proc_create("wireless", S_IRUGO, net->proc_net, +			 &wireless_seq_fops))  		return -ENOMEM;  	return 0; @@ -151,5 +152,5 @@ int __net_init wext_proc_init(struct net *net)  void __net_exit wext_proc_exit(struct net *net)  { -	proc_net_remove(net, "wireless"); +	remove_proc_entry("wireless", net->proc_net);  } diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 1f773f668d1..fb9622f6d99 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -119,7 +119,16 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,  	 * channel we disconnected above and reconnect below.  	 */  	if (chan && !wdev->wext.connect.ssid_len) { -		err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); +		struct cfg80211_chan_def chandef = { +			.width = NL80211_CHAN_WIDTH_20_NOHT, +			.center_freq1 = freq, +		}; + +		chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); +		if (chandef.chan) +			err = cfg80211_set_monitor_channel(rdev, &chandef); +		else +			err = -EINVAL;  		goto out;  	} @@ -233,13 +242,17 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev,  	wdev_lock(wdev);  	if (wdev->current_bss) { -		const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, -						    WLAN_EID_SSID); +		const u8 *ie; + +		rcu_read_lock(); +		ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, +					  WLAN_EID_SSID);  		if (ie) {  			data->flags = 1;  			data->length = ie[1];  			memcpy(ssid, ie + 2, data->length);  		} +		rcu_read_unlock();  	} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {  		data->flags = 1;  		data->length = wdev->wext.connect.ssid_len;  |