diff options
Diffstat (limited to 'net/wireless')
| -rw-r--r-- | net/wireless/Kconfig | 37 | ||||
| -rw-r--r-- | net/wireless/ap.c | 13 | ||||
| -rw-r--r-- | net/wireless/chan.c | 455 | ||||
| -rw-r--r-- | net/wireless/core.c | 191 | ||||
| -rw-r--r-- | net/wireless/core.h | 100 | ||||
| -rw-r--r-- | net/wireless/debugfs.c | 24 | ||||
| -rw-r--r-- | net/wireless/ethtool.c | 10 | ||||
| -rw-r--r-- | net/wireless/genregdb.awk | 73 | ||||
| -rw-r--r-- | net/wireless/ibss.c | 78 | ||||
| -rw-r--r-- | net/wireless/mesh.c | 27 | ||||
| -rw-r--r-- | net/wireless/mlme.c | 68 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 2017 | ||||
| -rw-r--r-- | net/wireless/nl80211.h | 15 | ||||
| -rw-r--r-- | net/wireless/radiotap.c | 11 | ||||
| -rw-r--r-- | net/wireless/rdev-ops.h | 51 | ||||
| -rw-r--r-- | net/wireless/reg.c | 1265 | ||||
| -rw-r--r-- | net/wireless/reg.h | 29 | ||||
| -rw-r--r-- | net/wireless/scan.c | 258 | ||||
| -rw-r--r-- | net/wireless/sme.c | 92 | ||||
| -rw-r--r-- | net/wireless/sysfs.h | 4 | ||||
| -rw-r--r-- | net/wireless/trace.h | 159 | ||||
| -rw-r--r-- | net/wireless/util.c | 303 | ||||
| -rw-r--r-- | net/wireless/wext-compat.c | 46 | ||||
| -rw-r--r-- | net/wireless/wext-compat.h | 2 | ||||
| -rw-r--r-- | net/wireless/wext-sme.c | 14 | 
25 files changed, 3588 insertions, 1754 deletions
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 16d08b39921..405f3c4cf70 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -95,6 +95,43 @@ config CFG80211_CERTIFICATION_ONUS  	  you are a wireless researcher and are working in a controlled  	  and approved environment by your local regulatory agency. +config CFG80211_REG_CELLULAR_HINTS +	bool "cfg80211 regulatory support for cellular base station hints" +	depends on CFG80211_CERTIFICATION_ONUS +	---help--- +	  This option enables support for parsing regulatory hints +	  from cellular base stations. If enabled and at least one driver +	  claims support for parsing cellular base station hints the +	  regulatory core will allow and parse these regulatory hints. +	  The regulatory core will only apply these regulatory hints on +	  drivers that support this feature. You should only enable this +	  feature if you have tested and validated this feature on your +	  systems. + +config CFG80211_REG_RELAX_NO_IR +	bool "cfg80211 support for NO_IR relaxation" +	depends on CFG80211_CERTIFICATION_ONUS +	---help--- +	 This option enables support for relaxation of the NO_IR flag for +	 situations that certain regulatory bodies have provided clarifications +	 on how relaxation can occur. This feature has an inherent dependency on +	 userspace features which must have been properly tested and as such is +	 not enabled by default. + +	 A relaxation feature example is allowing the operation of a P2P group +	 owner (GO) on channels marked with NO_IR if there is an additional BSS +	 interface which associated to an AP which userspace assumes or confirms +	 to be an authorized master, i.e., with radar detection support and DFS +	 capabilities. However, note that in order to not create daisy chain +	 scenarios, this relaxation is not allowed in cases that the BSS client +	 is associated to P2P GO and in addition the P2P GO instantiated on +	 a channel due to this relaxation should not allow connection from +	 non P2P clients. + +	 The regulatory core will apply these relaxations only for drivers that +	 support this feature by declaring the appropriate channel flags and +	 capabilities in their registration flow. +  config CFG80211_DEFAULT_PS  	bool "enable powersave by default"  	depends on CFG80211 diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 324e8d851dc..bdad1f95156 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -6,8 +6,8 @@  #include "rdev-ops.h" -static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, -			      struct net_device *dev) +int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, +		       struct net_device *dev, bool notify)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	int err; @@ -27,21 +27,24 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,  	err = rdev_stop_ap(rdev, dev);  	if (!err) {  		wdev->beacon_interval = 0; -		wdev->channel = NULL; +		memset(&wdev->chandef, 0, sizeof(wdev->chandef));  		wdev->ssid_len = 0; +		rdev_set_qos_map(rdev, dev, NULL); +		if (notify) +			nl80211_send_ap_stopped(wdev);  	}  	return err;  }  int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, -		     struct net_device *dev) +		     struct net_device *dev, bool notify)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	int err;  	wdev_lock(wdev); -	err = __cfg80211_stop_ap(rdev, dev); +	err = __cfg80211_stop_ap(rdev, dev, notify);  	wdev_unlock(wdev);  	return err; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 50f6195c8b7..992b34070bc 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -277,6 +277,32 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,  				     width, dfs_state);  } +static u32 cfg80211_get_start_freq(u32 center_freq, +				   u32 bandwidth) +{ +	u32 start_freq; + +	if (bandwidth <= 20) +		start_freq = center_freq; +	else +		start_freq = center_freq - bandwidth/2 + 10; + +	return start_freq; +} + +static u32 cfg80211_get_end_freq(u32 center_freq, +				 u32 bandwidth) +{ +	u32 end_freq; + +	if (bandwidth <= 20) +		end_freq = center_freq; +	else +		end_freq = center_freq + bandwidth/2 - 10; + +	return end_freq; +} +  static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,  					    u32 center_freq,  					    u32 bandwidth) @@ -284,13 +310,8 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,  	struct ieee80211_channel *c;  	u32 freq, start_freq, end_freq; -	if (bandwidth <= 20) { -		start_freq = center_freq; -		end_freq = center_freq; -	} else { -		start_freq = center_freq - bandwidth/2 + 10; -		end_freq = center_freq + bandwidth/2 - 10; -	} +	start_freq = cfg80211_get_start_freq(center_freq, bandwidth); +	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);  	for (freq = start_freq; freq <= end_freq; freq += 20) {  		c = ieee80211_get_channel(wiphy, freq); @@ -305,57 +326,269 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,  int cfg80211_chandef_dfs_required(struct wiphy *wiphy, -				  const struct cfg80211_chan_def *chandef) +				  const struct cfg80211_chan_def *chandef, +				  enum nl80211_iftype iftype)  {  	int width; -	int r; +	int ret;  	if (WARN_ON(!cfg80211_chandef_valid(chandef)))  		return -EINVAL; +	switch (iftype) { +	case NL80211_IFTYPE_ADHOC: +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_P2P_GO: +	case NL80211_IFTYPE_MESH_POINT: +		width = cfg80211_chandef_get_width(chandef); +		if (width < 0) +			return -EINVAL; + +		ret = cfg80211_get_chans_dfs_required(wiphy, +						      chandef->center_freq1, +						      width); +		if (ret < 0) +			return ret; +		else if (ret > 0) +			return BIT(chandef->width); + +		if (!chandef->center_freq2) +			return 0; + +		ret = cfg80211_get_chans_dfs_required(wiphy, +						      chandef->center_freq2, +						      width); +		if (ret < 0) +			return ret; +		else if (ret > 0) +			return BIT(chandef->width); + +		break; +	case NL80211_IFTYPE_STATION: +	case NL80211_IFTYPE_P2P_CLIENT: +	case NL80211_IFTYPE_MONITOR: +	case NL80211_IFTYPE_AP_VLAN: +	case NL80211_IFTYPE_WDS: +	case NL80211_IFTYPE_P2P_DEVICE: +		break; +	case NL80211_IFTYPE_UNSPECIFIED: +	case NUM_NL80211_IFTYPES: +		WARN_ON(1); +	} + +	return 0; +} +EXPORT_SYMBOL(cfg80211_chandef_dfs_required); + +static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy, +					 u32 center_freq, +					 u32 bandwidth) +{ +	struct ieee80211_channel *c; +	u32 freq, start_freq, end_freq; +	int count = 0; + +	start_freq = cfg80211_get_start_freq(center_freq, bandwidth); +	end_freq = cfg80211_get_end_freq(center_freq, bandwidth); + +	/* +	 * Check entire range of channels for the bandwidth. +	 * Check all channels are DFS channels (DFS_USABLE or +	 * DFS_AVAILABLE). Return number of usable channels +	 * (require CAC). Allow DFS and non-DFS channel mix. +	 */ +	for (freq = start_freq; freq <= end_freq; freq += 20) { +		c = ieee80211_get_channel(wiphy, freq); +		if (!c) +			return -EINVAL; + +		if (c->flags & IEEE80211_CHAN_DISABLED) +			return -EINVAL; + +		if (c->flags & IEEE80211_CHAN_RADAR) { +			if (c->dfs_state == NL80211_DFS_UNAVAILABLE) +				return -EINVAL; + +			if (c->dfs_state == NL80211_DFS_USABLE) +				count++; +		} +	} + +	return count; +} + +bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, +				 const struct cfg80211_chan_def *chandef) +{ +	int width; +	int r1, r2 = 0; + +	if (WARN_ON(!cfg80211_chandef_valid(chandef))) +		return false; +  	width = cfg80211_chandef_get_width(chandef);  	if (width < 0) -		return -EINVAL; +		return false; -	r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1, -					    width); -	if (r) -		return r; +	r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1, +					  width); -	if (!chandef->center_freq2) -		return 0; +	if (r1 < 0) +		return false; -	return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, -					       width); +	switch (chandef->width) { +	case NL80211_CHAN_WIDTH_80P80: +		WARN_ON(!chandef->center_freq2); +		r2 = cfg80211_get_chans_dfs_usable(wiphy, +						   chandef->center_freq2, +						   width); +		if (r2 < 0) +			return false; +		break; +	default: +		WARN_ON(chandef->center_freq2); +		break; +	} + +	return (r1 + r2 > 0);  } -static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, -					u32 center_freq, u32 bandwidth, -					u32 prohibited_flags) + +static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, +					     u32 center_freq, +					     u32 bandwidth)  {  	struct ieee80211_channel *c;  	u32 freq, start_freq, end_freq; -	if (bandwidth <= 20) { -		start_freq = center_freq; -		end_freq = center_freq; -	} else { -		start_freq = center_freq - bandwidth/2 + 10; -		end_freq = center_freq + bandwidth/2 - 10; -	} +	start_freq = cfg80211_get_start_freq(center_freq, bandwidth); +	end_freq = cfg80211_get_end_freq(center_freq, bandwidth); +	/* +	 * Check entire range of channels for the bandwidth. +	 * If any channel in between is disabled or has not +	 * had gone through CAC return false +	 */  	for (freq = start_freq; freq <= end_freq; freq += 20) {  		c = ieee80211_get_channel(wiphy, freq);  		if (!c)  			return false; -		/* check for radar flags */ -		if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) && +		if (c->flags & IEEE80211_CHAN_DISABLED) +			return false; + +		if ((c->flags & IEEE80211_CHAN_RADAR)  &&  		    (c->dfs_state != NL80211_DFS_AVAILABLE))  			return false; +	} + +	return true; +} -		/* check for the other flags */ -		if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR) +static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy, +				const struct cfg80211_chan_def *chandef) +{ +	int width; +	int r; + +	if (WARN_ON(!cfg80211_chandef_valid(chandef))) +		return false; + +	width = cfg80211_chandef_get_width(chandef); +	if (width < 0) +		return false; + +	r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1, +					     width); + +	/* If any of channels unavailable for cf1 just return */ +	if (!r) +		return r; + +	switch (chandef->width) { +	case NL80211_CHAN_WIDTH_80P80: +		WARN_ON(!chandef->center_freq2); +		r = cfg80211_get_chans_dfs_available(wiphy, +						     chandef->center_freq2, +						     width); +	default: +		WARN_ON(chandef->center_freq2); +		break; +	} + +	return r; +} + +static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy, +						    u32 center_freq, +						    u32 bandwidth) +{ +	struct ieee80211_channel *c; +	u32 start_freq, end_freq, freq; +	unsigned int dfs_cac_ms = 0; + +	start_freq = cfg80211_get_start_freq(center_freq, bandwidth); +	end_freq = cfg80211_get_end_freq(center_freq, bandwidth); + +	for (freq = start_freq; freq <= end_freq; freq += 20) { +		c = ieee80211_get_channel(wiphy, freq); +		if (!c) +			return 0; + +		if (c->flags & IEEE80211_CHAN_DISABLED) +			return 0; + +		if (!(c->flags & IEEE80211_CHAN_RADAR)) +			continue; + +		if (c->dfs_cac_ms > dfs_cac_ms) +			dfs_cac_ms = c->dfs_cac_ms; +	} + +	return dfs_cac_ms; +} + +unsigned int +cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, +			      const struct cfg80211_chan_def *chandef) +{ +	int width; +	unsigned int t1 = 0, t2 = 0; + +	if (WARN_ON(!cfg80211_chandef_valid(chandef))) +		return 0; + +	width = cfg80211_chandef_get_width(chandef); +	if (width < 0) +		return 0; + +	t1 = cfg80211_get_chans_dfs_cac_time(wiphy, +					     chandef->center_freq1, +					     width); + +	if (!chandef->center_freq2) +		return t1; + +	t2 = cfg80211_get_chans_dfs_cac_time(wiphy, +					     chandef->center_freq2, +					     width); + +	return max(t1, t2); +} + +static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, +					u32 center_freq, u32 bandwidth, +					u32 prohibited_flags) +{ +	struct ieee80211_channel *c; +	u32 freq, start_freq, end_freq; + +	start_freq = cfg80211_get_start_freq(center_freq, bandwidth); +	end_freq = cfg80211_get_end_freq(center_freq, bandwidth); + +	for (freq = start_freq; freq <= end_freq; freq += 20) { +		c = ieee80211_get_channel(wiphy, freq); +		if (!c || c->flags & prohibited_flags)  			return false;  	} @@ -383,12 +616,14 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,  		width = 5;  		break;  	case NL80211_CHAN_WIDTH_10: +		prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;  		width = 10;  		break;  	case NL80211_CHAN_WIDTH_20:  		if (!ht_cap->ht_supported)  			return false;  	case NL80211_CHAN_WIDTH_20_NOHT: +		prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;  		width = 20;  		break;  	case NL80211_CHAN_WIDTH_40: @@ -457,18 +692,117 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,  }  EXPORT_SYMBOL(cfg80211_chandef_usable); +/* + * For GO only, check if the channel can be used under permissive conditions + * mandated by the some regulatory bodies, i.e., the channel is marked with + * IEEE80211_CHAN_GO_CONCURRENT and there is an additional station interface + * associated to an AP on the same channel or on the same UNII band + * (assuming that the AP is an authorized master). + * In addition allow the GO to operate on a channel on which indoor operation is + * allowed, iff we are currently operating in an indoor environment. + */ +static bool cfg80211_go_permissive_chan(struct cfg80211_registered_device *rdev, +					struct ieee80211_channel *chan) +{ +	struct wireless_dev *wdev_iter; +	struct wiphy *wiphy = wiphy_idx_to_wiphy(rdev->wiphy_idx); + +	ASSERT_RTNL(); + +	if (!config_enabled(CONFIG_CFG80211_REG_RELAX_NO_IR) || +	    !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR)) +		return false; + +	if (regulatory_indoor_allowed() && +	    (chan->flags & IEEE80211_CHAN_INDOOR_ONLY)) +		return true; + +	if (!(chan->flags & IEEE80211_CHAN_GO_CONCURRENT)) +		return false; + +	/* +	 * Generally, it is possible to rely on another device/driver to allow +	 * the GO concurrent relaxation, however, since the device can further +	 * enforce the relaxation (by doing a similar verifications as this), +	 * and thus fail the GO instantiation, consider only the interfaces of +	 * the current registered device. +	 */ +	list_for_each_entry(wdev_iter, &rdev->wdev_list, list) { +		struct ieee80211_channel *other_chan = NULL; +		int r1, r2; + +		if (wdev_iter->iftype != NL80211_IFTYPE_STATION || +		    !netif_running(wdev_iter->netdev)) +			continue; + +		wdev_lock(wdev_iter); +		if (wdev_iter->current_bss) +			other_chan = wdev_iter->current_bss->pub.channel; +		wdev_unlock(wdev_iter); + +		if (!other_chan) +			continue; + +		if (chan == other_chan) +			return true; + +		if (chan->band != IEEE80211_BAND_5GHZ) +			continue; + +		r1 = cfg80211_get_unii(chan->center_freq); +		r2 = cfg80211_get_unii(other_chan->center_freq); + +		if (r1 != -EINVAL && r1 == r2) { +			/* +			 * At some locations channels 149-165 are considered a +			 * bundle, but at other locations, e.g., Indonesia, +			 * channels 149-161 are considered a bundle while +			 * channel 165 is left out and considered to be in a +			 * different bundle. Thus, in case that there is a +			 * station interface connected to an AP on channel 165, +			 * it is assumed that channels 149-161 are allowed for +			 * GO operations. However, having a station interface +			 * connected to an AP on channels 149-161, does not +			 * allow GO operation on channel 165. +			 */ +			if (chan->center_freq == 5825 && +			    other_chan->center_freq != 5825) +				continue; +			return true; +		} +	} + +	return false; +} +  bool cfg80211_reg_can_beacon(struct wiphy *wiphy, -			     struct cfg80211_chan_def *chandef) +			     struct cfg80211_chan_def *chandef, +			     enum nl80211_iftype iftype)  { +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	bool res; +	u32 prohibited_flags = IEEE80211_CHAN_DISABLED | +			       IEEE80211_CHAN_RADAR; -	trace_cfg80211_reg_can_beacon(wiphy, chandef); +	trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype); -	res = cfg80211_chandef_usable(wiphy, chandef, -				      IEEE80211_CHAN_DISABLED | -				      IEEE80211_CHAN_PASSIVE_SCAN | -				      IEEE80211_CHAN_NO_IBSS | -				      IEEE80211_CHAN_RADAR); +	/* +	 * Under certain conditions suggested by the some regulatory bodies +	 * a GO can operate on channels marked with IEEE80211_NO_IR +	 * so set this flag only if such relaxations are not enabled and +	 * the conditions are not met. +	 */ +	if (iftype != NL80211_IFTYPE_P2P_GO || +	    !cfg80211_go_permissive_chan(rdev, chandef->chan)) +		prohibited_flags |= IEEE80211_CHAN_NO_IR; + +	if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 && +	    cfg80211_chandef_dfs_available(wiphy, chandef)) { +		/* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */ +		prohibited_flags = IEEE80211_CHAN_DISABLED; +	} + +	res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);  	trace_cfg80211_return_bool(res);  	return res; @@ -489,8 +823,11 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,  void  cfg80211_get_chan_state(struct wireless_dev *wdev,  		        struct ieee80211_channel **chan, -		        enum cfg80211_chan_mode *chanmode) +		        enum cfg80211_chan_mode *chanmode, +		        u8 *radar_detect)  { +	int ret; +  	*chan = NULL;  	*chanmode = CHAN_MODE_UNDEFINED; @@ -503,11 +840,18 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,  	case NL80211_IFTYPE_ADHOC:  		if (wdev->current_bss) {  			*chan = wdev->current_bss->pub.channel; -			*chanmode = wdev->ibss_fixed +			*chanmode = (wdev->ibss_fixed && +				     !wdev->ibss_dfs_possible)  				  ? CHAN_MODE_SHARED  				  : CHAN_MODE_EXCLUSIVE; + +			/* consider worst-case - IBSS can try to return to the +			 * original user-specified channel as creator */ +			if (wdev->ibss_dfs_possible) +				*radar_detect |= BIT(wdev->chandef.width);  			return;  		} +		break;  	case NL80211_IFTYPE_STATION:  	case NL80211_IFTYPE_P2P_CLIENT:  		if (wdev->current_bss) { @@ -519,33 +863,42 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_P2P_GO:  		if (wdev->cac_started) { -			*chan = wdev->channel; +			*chan = wdev->chandef.chan;  			*chanmode = CHAN_MODE_SHARED; +			*radar_detect |= BIT(wdev->chandef.width);  		} else if (wdev->beacon_interval) { -			*chan = wdev->channel; +			*chan = wdev->chandef.chan;  			*chanmode = CHAN_MODE_SHARED; + +			ret = cfg80211_chandef_dfs_required(wdev->wiphy, +							    &wdev->chandef, +							    wdev->iftype); +			WARN_ON(ret < 0); +			if (ret > 0) +				*radar_detect |= BIT(wdev->chandef.width);  		}  		return;  	case NL80211_IFTYPE_MESH_POINT:  		if (wdev->mesh_id_len) { -			*chan = wdev->channel; +			*chan = wdev->chandef.chan;  			*chanmode = CHAN_MODE_SHARED; + +			ret = cfg80211_chandef_dfs_required(wdev->wiphy, +							    &wdev->chandef, +							    wdev->iftype); +			WARN_ON(ret < 0); +			if (ret > 0) +				*radar_detect |= BIT(wdev->chandef.width);  		}  		return;  	case NL80211_IFTYPE_MONITOR:  	case NL80211_IFTYPE_AP_VLAN:  	case NL80211_IFTYPE_WDS: -		/* these interface types don't really have a channel */ -		return;  	case NL80211_IFTYPE_P2P_DEVICE: -		if (wdev->wiphy->features & -				NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL) -			*chanmode = CHAN_MODE_EXCLUSIVE; +		/* these interface types don't really have a channel */  		return;  	case NL80211_IFTYPE_UNSPECIFIED:  	case NUM_NL80211_IFTYPES:  		WARN_ON(1);  	} - -	return;  } diff --git a/net/wireless/core.c b/net/wireless/core.c index 67153964aad..a1c40654dd9 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -69,7 +69,7 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)  int get_wiphy_idx(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	return rdev->wiphy_idx;  } @@ -130,7 +130,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,  			    newname))  		pr_err("failed to rename debugfs dir to %s!\n", newname); -	nl80211_notify_dev_rename(rdev); +	nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);  	return 0;  } @@ -204,27 +204,18 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,  	rdev->opencount--;  	if (rdev->scan_req && rdev->scan_req->wdev == wdev) { -		/* -		 * If the scan request wasn't notified as done, set it -		 * to aborted and leak it after a warning. The driver -		 * should have notified us that it ended at the latest -		 * during rdev_stop_p2p_device(). -		 */  		if (WARN_ON(!rdev->scan_req->notified))  			rdev->scan_req->aborted = true; -		___cfg80211_scan_done(rdev, !rdev->scan_req->notified); +		___cfg80211_scan_done(rdev, false);  	}  } -static int cfg80211_rfkill_set_block(void *data, bool blocked) +void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = data; +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct wireless_dev *wdev; -	if (!blocked) -		return 0; - -	rtnl_lock(); +	ASSERT_RTNL();  	list_for_each_entry(wdev, &rdev->wdev_list, list) {  		if (wdev->netdev) { @@ -240,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)  			break;  		}  	} +} +EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces); +static int cfg80211_rfkill_set_block(void *data, bool blocked) +{ +	struct cfg80211_registered_device *rdev = data; + +	if (!blocked) +		return 0; + +	rtnl_lock(); +	cfg80211_shutdown_all_interfaces(&rdev->wiphy);  	rtnl_unlock();  	return 0; @@ -266,6 +268,45 @@ static void cfg80211_event_work(struct work_struct *work)  	rtnl_unlock();  } +void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) +{ +	struct cfg80211_iface_destroy *item; + +	ASSERT_RTNL(); + +	spin_lock_irq(&rdev->destroy_list_lock); +	while ((item = list_first_entry_or_null(&rdev->destroy_list, +						struct cfg80211_iface_destroy, +						list))) { +		struct wireless_dev *wdev, *tmp; +		u32 nlportid = item->nlportid; + +		list_del(&item->list); +		kfree(item); +		spin_unlock_irq(&rdev->destroy_list_lock); + +		list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) { +			if (nlportid == wdev->owner_nlportid) +				rdev_del_virtual_intf(rdev, wdev); +		} + +		spin_lock_irq(&rdev->destroy_list_lock); +	} +	spin_unlock_irq(&rdev->destroy_list_lock); +} + +static void cfg80211_destroy_iface_wk(struct work_struct *work) +{ +	struct cfg80211_registered_device *rdev; + +	rdev = container_of(work, struct cfg80211_registered_device, +			    destroy_work); + +	rtnl_lock(); +	cfg80211_destroy_ifaces(rdev); +	rtnl_unlock(); +} +  /* exported functions */  struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) @@ -324,6 +365,10 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)  	rdev->wiphy.dev.class = &ieee80211_class;  	rdev->wiphy.dev.platform_data = rdev; +	INIT_LIST_HEAD(&rdev->destroy_list); +	spin_lock_init(&rdev->destroy_list_lock); +	INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); +  #ifdef CONFIG_CFG80211_DEFAULT_PS  	rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;  #endif @@ -357,7 +402,7 @@ 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; +	rdev->wiphy.max_num_csa_counters = 1;  	return &rdev->wiphy;  } @@ -404,10 +449,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)  		for (j = 0; j < c->n_limits; j++) {  			u16 types = c->limits[j].types; -			/* -			 * interface types shouldn't overlap, this is -			 * used in cfg80211_can_change_interface() -			 */ +			/* interface types shouldn't overlap */  			if (WARN_ON(types & all_iftypes))  				return -EINVAL;  			all_iftypes |= types; @@ -443,7 +485,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)  int wiphy_register(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	int res;  	enum ieee80211_band band;  	struct ieee80211_supported_band *sband; @@ -451,6 +493,12 @@ int wiphy_register(struct wiphy *wiphy)  	int i;  	u16 ifmodes = wiphy->interface_modes; +	/* +	 * There are major locking problems in nl80211/mac80211 for CSA, +	 * disable for all drivers until this has been reworked. +	 */ +	wiphy->flags &= ~WIPHY_FLAG_HAS_CHANNEL_SWITCH; +  #ifdef CONFIG_PM  	if (WARN_ON(wiphy->wowlan &&  		    (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && @@ -566,18 +614,15 @@ int wiphy_register(struct wiphy *wiphy)  	/* check and set up bitrates */  	ieee80211_set_bitrate_flags(wiphy); +	rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH; +	rtnl_lock();  	res = device_add(&rdev->wiphy.dev); -	if (res) -		return res; - -	res = rfkill_register(rdev->rfkill);  	if (res) { -		device_del(&rdev->wiphy.dev); +		rtnl_unlock();  		return res;  	} -	rtnl_lock();  	/* set up regulatory info */  	wiphy_regulatory_register(wiphy); @@ -591,7 +636,7 @@ int wiphy_register(struct wiphy *wiphy)  	if (IS_ERR(rdev->wiphy.debugfsdir))  		rdev->wiphy.debugfsdir = NULL; -	if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { +	if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {  		struct regulatory_request request;  		request.wiphy_idx = get_wiphy_idx(wiphy); @@ -606,13 +651,24 @@ int wiphy_register(struct wiphy *wiphy)  	rdev->wiphy.registered = true;  	rtnl_unlock(); + +	res = rfkill_register(rdev->rfkill); +	if (res) { +		rfkill_destroy(rdev->rfkill); +		rdev->rfkill = NULL; +		wiphy_unregister(&rdev->wiphy); +		return res; +	} + +	nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); +  	return 0;  }  EXPORT_SYMBOL(wiphy_register);  void wiphy_rfkill_start_polling(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	if (!rdev->ops->rfkill_poll)  		return; @@ -623,7 +679,7 @@ EXPORT_SYMBOL(wiphy_rfkill_start_polling);  void wiphy_rfkill_stop_polling(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	rfkill_pause_polling(rdev->rfkill);  } @@ -631,7 +687,7 @@ EXPORT_SYMBOL(wiphy_rfkill_stop_polling);  void wiphy_unregister(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	wait_event(rdev->dev_wait, ({  		int __count; @@ -640,12 +696,14 @@ void wiphy_unregister(struct wiphy *wiphy)  		rtnl_unlock();  		__count == 0; })); -	rfkill_unregister(rdev->rfkill); +	if (rdev->rfkill) +		rfkill_unregister(rdev->rfkill);  	rtnl_lock(); +	nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);  	rdev->wiphy.registered = false; -	BUG_ON(!list_empty(&rdev->wdev_list)); +	WARN_ON(!list_empty(&rdev->wdev_list));  	/*  	 * First remove the hardware from everywhere, this makes @@ -670,6 +728,7 @@ void wiphy_unregister(struct wiphy *wiphy)  	cancel_work_sync(&rdev->conn_work);  	flush_work(&rdev->event_work);  	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); +	flush_work(&rdev->destroy_work);  #ifdef CONFIG_PM  	if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) @@ -702,7 +761,7 @@ EXPORT_SYMBOL(wiphy_free);  void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	if (rfkill_set_hw_state(rdev->rfkill, blocked))  		schedule_work(&rdev->rfkill_sync); @@ -711,7 +770,7 @@ EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);  void cfg80211_unregister_wdev(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	ASSERT_RTNL(); @@ -732,7 +791,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)  }  EXPORT_SYMBOL(cfg80211_unregister_wdev); -static struct device_type wiphy_type = { +static const struct device_type wiphy_type = {  	.name	= "wlan",  }; @@ -746,20 +805,23 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,  		rdev->num_running_monitor_ifaces += num;  } -void cfg80211_leave(struct cfg80211_registered_device *rdev, -		    struct wireless_dev *wdev) +void __cfg80211_leave(struct cfg80211_registered_device *rdev, +		      struct wireless_dev *wdev)  {  	struct net_device *dev = wdev->netdev; +	ASSERT_RTNL(); +	ASSERT_WDEV_LOCK(wdev); +  	switch (wdev->iftype) {  	case NL80211_IFTYPE_ADHOC: -		cfg80211_leave_ibss(rdev, dev, true); +		__cfg80211_leave_ibss(rdev, dev, true);  		break;  	case NL80211_IFTYPE_P2P_CLIENT:  	case NL80211_IFTYPE_STATION: -		__cfg80211_stop_sched_scan(rdev, false); +		if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) +			__cfg80211_stop_sched_scan(rdev, false); -		wdev_lock(wdev);  #ifdef CONFIG_CFG80211_WEXT  		kfree(wdev->wext.ie);  		wdev->wext.ie = NULL; @@ -768,34 +830,60 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,  #endif  		cfg80211_disconnect(rdev, dev,  				    WLAN_REASON_DEAUTH_LEAVING, true); -		wdev_unlock(wdev);  		break;  	case NL80211_IFTYPE_MESH_POINT: -		cfg80211_leave_mesh(rdev, dev); +		__cfg80211_leave_mesh(rdev, dev);  		break;  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_P2P_GO: -		cfg80211_stop_ap(rdev, dev); +		__cfg80211_stop_ap(rdev, dev, true);  		break;  	default:  		break;  	} +} -	wdev->beacon_interval = 0; +void cfg80211_leave(struct cfg80211_registered_device *rdev, +		    struct wireless_dev *wdev) +{ +	wdev_lock(wdev); +	__cfg80211_leave(rdev, wdev); +	wdev_unlock(wdev);  } +void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, +			 gfp_t gfp) +{ +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); +	struct cfg80211_event *ev; +	unsigned long flags; + +	trace_cfg80211_stop_iface(wiphy, wdev); + +	ev = kzalloc(sizeof(*ev), gfp); +	if (!ev) +		return; + +	ev->type = EVENT_STOPPED; + +	spin_lock_irqsave(&wdev->event_lock, flags); +	list_add_tail(&ev->list, &wdev->event_list); +	spin_unlock_irqrestore(&wdev->event_lock, flags); +	queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_stop_iface); +  static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  					 unsigned long state, void *ptr)  {  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_registered_device *rdev; -	int ret;  	if (!wdev)  		return NOTIFY_DONE; -	rdev = wiphy_to_dev(wdev->wiphy); +	rdev = wiphy_to_rdev(wdev->wiphy);  	WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); @@ -854,7 +942,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  		if (rdev->scan_req && rdev->scan_req->wdev == wdev) {  			if (WARN_ON(!rdev->scan_req->notified))  				rdev->scan_req->aborted = true; -			___cfg80211_scan_done(rdev, true); +			___cfg80211_scan_done(rdev, false);  		}  		if (WARN_ON(rdev->sched_scan_req && @@ -955,13 +1043,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,  			return notifier_from_errno(-EOPNOTSUPP);  		if (rfkill_blocked(rdev->rfkill))  			return notifier_from_errno(-ERFKILL); -		ret = cfg80211_can_add_interface(rdev, wdev->iftype); -		if (ret) -			return notifier_from_errno(ret);  		break; +	default: +		return NOTIFY_DONE;  	} -	return NOTIFY_DONE; +	return NOTIFY_OK;  }  static struct notifier_block cfg80211_netdev_notifier = { diff --git a/net/wireless/core.h b/net/wireless/core.h index 9ad43c619c5..7e3a3cef7df 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -62,14 +62,13 @@ struct cfg80211_registered_device {  	struct rb_root bss_tree;  	u32 bss_generation;  	struct cfg80211_scan_request *scan_req; /* protected by RTNL */ +	struct sk_buff *scan_msg;  	struct cfg80211_sched_scan_request *sched_scan_req;  	unsigned long suspend_at;  	struct work_struct scan_done_wk;  	struct work_struct sched_scan_results_wk; -#ifdef CONFIG_NL80211_TESTMODE -	struct genl_info *testmode_info; -#endif +	struct genl_info *cur_cmd_info;  	struct work_struct conn_work;  	struct work_struct event_work; @@ -81,13 +80,17 @@ struct cfg80211_registered_device {  	struct cfg80211_coalesce *coalesce; +	spinlock_t destroy_list_lock; +	struct list_head destroy_list; +	struct work_struct destroy_work; +  	/* must be last because of the way we do wiphy_priv(),  	 * and it should at least be aligned to NETDEV_ALIGN */  	struct wiphy wiphy __aligned(NETDEV_ALIGN);  };  static inline -struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) +struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)  {  	BUG_ON(!wiphy);  	return container_of(wiphy, struct cfg80211_registered_device, wiphy); @@ -167,7 +170,6 @@ static inline void wdev_unlock(struct wireless_dev *wdev)  	mutex_unlock(&wdev->mtx);  } -#define ASSERT_RDEV_LOCK(rdev) ASSERT_RTNL()  #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)  static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) @@ -183,6 +185,7 @@ enum cfg80211_event_type {  	EVENT_ROAMED,  	EVENT_DISCONNECTED,  	EVENT_IBSS_JOINED, +	EVENT_STOPPED,  };  struct cfg80211_event { @@ -212,6 +215,7 @@ struct cfg80211_event {  		} dc;  		struct {  			u8 bssid[ETH_ALEN]; +			struct ieee80211_channel *channel;  		} ij;  	};  }; @@ -233,23 +237,26 @@ struct cfg80211_beacon_registration {  	u32 nlportid;  }; +struct cfg80211_iface_destroy { +	struct list_head list; +	u32 nlportid; +}; + +void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); +  /* free object */ -extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); +void cfg80211_dev_free(struct cfg80211_registered_device *rdev); -extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, -			       char *newname); +int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, +			char *newname);  void ieee80211_set_bitrate_flags(struct wiphy *wiphy); -void cfg80211_bss_expire(struct cfg80211_registered_device *dev); -void cfg80211_bss_age(struct cfg80211_registered_device *dev, +void cfg80211_bss_expire(struct cfg80211_registered_device *rdev); +void cfg80211_bss_age(struct cfg80211_registered_device *rdev,                        unsigned long age_secs);  /* IBSS */ -int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, -			 struct net_device *dev, -			 struct cfg80211_ibss_params *params, -			 struct cfg80211_cached_keys *connkeys);  int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  		       struct net_device *dev,  		       struct cfg80211_ibss_params *params, @@ -259,7 +266,8 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,  			  struct net_device *dev, bool nowext);  int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,  			struct net_device *dev, bool nowext); -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, +			    struct ieee80211_channel *channel);  int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,  			    struct wireless_dev *wdev); @@ -274,6 +282,8 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,  		       struct net_device *dev,  		       struct mesh_setup *setup,  		       const struct mesh_config *conf); +int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, +			  struct net_device *dev);  int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,  			struct net_device *dev);  int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, @@ -281,8 +291,10 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,  			      struct cfg80211_chan_def *chandef);  /* AP */ +int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, +		       struct net_device *dev, bool notify);  int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, -		     struct net_device *dev); +		     struct net_device *dev, bool notify);  /* MLME */  int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, @@ -317,9 +329,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);  void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);  int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,  			  struct wireless_dev *wdev, -			  struct ieee80211_channel *chan, bool offchan, -			  unsigned int wait, const u8 *buf, size_t len, -			  bool no_cck, bool dont_wait_for_ack, u64 *cookie); +			  struct cfg80211_mgmt_tx_params *params, +			  u64 *cookie);  void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,  			       const struct ieee80211_ht_cap *ht_capa_mask);  void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, @@ -364,7 +375,8 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,  				   struct key_params *params, int key_idx,  				   bool pairwise, const u8 *mac_addr);  void __cfg80211_scan_done(struct work_struct *wk); -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, +			   bool send_message);  void __cfg80211_sched_scan_results(struct work_struct *wk);  int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,  			       bool driver_initiated); @@ -383,13 +395,17 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  				 u8 radar_detect);  /** - * cfg80211_chandef_dfs_required - checks if radar detection is required + * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable   * @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 + * + * Checks if chandef is usable and we can/need start CAC on such channel. + * + * Return: Return true if all channels available and at least + *	   one channel require CAC (NL80211_DFS_USABLE)   */ -int cfg80211_chandef_dfs_required(struct wiphy *wiphy, -				  const struct cfg80211_chan_def *c); +bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, +				 const struct cfg80211_chan_def *chandef);  void cfg80211_set_dfs_state(struct wiphy *wiphy,  			    const struct cfg80211_chan_def *chandef, @@ -397,32 +413,9 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,  void cfg80211_dfs_channels_update_work(struct work_struct *work); - -static inline int -cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, -			      struct wireless_dev *wdev, -			      enum nl80211_iftype iftype) -{ -	return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, -					    CHAN_MODE_UNDEFINED, 0); -} - -static inline int -cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, -			   enum nl80211_iftype iftype) -{ -	return cfg80211_can_change_interface(rdev, NULL, iftype); -} - -static inline int -cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, -		      struct wireless_dev *wdev, -		      struct ieee80211_channel *chan, -		      enum cfg80211_chan_mode chanmode) -{ -	return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, -					    chan, chanmode, 0); -} +unsigned int +cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, +			      const struct cfg80211_chan_def *chandef);  static inline unsigned int elapsed_jiffies_msecs(unsigned long start)  { @@ -431,13 +424,14 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start)  	if (end >= start)  		return jiffies_to_msecs(end - start); -	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); +	return jiffies_to_msecs(end + (ULONG_MAX - start) + 1);  }  void  cfg80211_get_chan_state(struct wireless_dev *wdev,  		        struct ieee80211_channel **chan, -		        enum cfg80211_chan_mode *chanmode); +		        enum cfg80211_chan_mode *chanmode, +		        u8 *radar_detect);  int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,  				 struct cfg80211_chan_def *chandef); @@ -452,6 +446,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,  void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,  			       enum nl80211_iftype iftype, int num); +void __cfg80211_leave(struct cfg80211_registered_device *rdev, +		      struct wireless_dev *wdev);  void cfg80211_leave(struct cfg80211_registered_device *rdev,  		    struct wireless_dev *wdev); diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 90d05003662..454157717ef 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -47,17 +47,19 @@ static int ht_print_chan(struct ieee80211_channel *chan,  		return 0;  	if (chan->flags & IEEE80211_CHAN_DISABLED) -		return snprintf(buf + offset, -				buf_size - offset, -				"%d Disabled\n", -				chan->center_freq); - -	return snprintf(buf + offset, -			buf_size - offset, -			"%d HT40 %c%c\n", -			chan->center_freq, -			(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', -			(chan->flags & IEEE80211_CHAN_NO_HT40PLUS)  ? ' ' : '+'); +		return scnprintf(buf + offset, +				 buf_size - offset, +				 "%d Disabled\n", +				 chan->center_freq); + +	return scnprintf(buf + offset, +			 buf_size - offset, +			 "%d HT40 %c%c\n", +			 chan->center_freq, +			 (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? +				' ' : '-', +			 (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? +				' ' : '+');  }  static ssize_t ht40allow_map_read(struct file *file, diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index e37862f1b12..d4860bfc020 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -43,7 +43,7 @@ static void cfg80211_get_ringparam(struct net_device *dev,  				   struct ethtool_ringparam *rp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	memset(rp, 0, sizeof(*rp)); @@ -56,7 +56,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,  				  struct ethtool_ringparam *rp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)  		return -EINVAL; @@ -70,7 +70,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,  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); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (rdev->ops->get_et_sset_count)  		return rdev_get_et_sset_count(rdev, dev, sset);  	return -EOPNOTSUPP; @@ -80,7 +80,7 @@ static void cfg80211_get_stats(struct net_device *dev,  			       struct ethtool_stats *stats, u64 *data)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (rdev->ops->get_et_stats)  		rdev_get_et_stats(rdev, dev, stats, data);  } @@ -88,7 +88,7 @@ static void cfg80211_get_stats(struct net_device *dev,  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); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (rdev->ops->get_et_strings)  		rdev_get_et_strings(rdev, dev, sset, data);  } diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index 9392f8cbb90..40c37fc5b67 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -33,25 +33,24 @@ BEGIN {  	regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"  } -/^[ \t]*#/ { -	# Ignore -} - -!active && /^[ \t]*$/ { -	# Ignore -} - -!active && /country/ { +function parse_country_head() {  	country=$2  	sub(/:/, "", country)  	printf "static const struct ieee80211_regdomain regdom_%s = {\n", country  	printf "\t.alpha2 = \"%s\",\n", country +	if ($NF ~ /DFS-ETSI/) +		printf "\t.dfs_region = NL80211_DFS_ETSI,\n" +	else if ($NF ~ /DFS-FCC/) +		printf "\t.dfs_region = NL80211_DFS_FCC,\n" +	else if ($NF ~ /DFS-JP/) +		printf "\t.dfs_region = NL80211_DFS_JP,\n"  	printf "\t.reg_rules = {\n"  	active = 1  	regdb = regdb "\t®dom_" country ",\n"  } -active && /^[ \t]*\(/ { +function parse_reg_rule() +{  	start = $1  	sub(/\(/, "", start)  	end = $3 @@ -67,19 +66,15 @@ active && /^[ \t]*\(/ {  	units = $8  	sub(/\)/, "", units)  	sub(/,/, "", units) +	dfs_cac = $9  	if (units == "mW") { -		if (power == 100) { -			power = 20 -		} else if (power == 200) { -			power = 23 -		} else if (power == 500) { -			power = 27 -		} else if (power == 1000) { -			power = 30 -		} else { -			print "Unknown power value in database!" -		} +		power = 10 * log(power)/log(10) +	} else { +		dfs_cac = $8  	} +	sub(/,/, "", dfs_cac) +	sub(/\(/, "", dfs_cac) +	sub(/\)/, "", dfs_cac)  	flagstr = ""  	for (i=8; i<=NF; i++)  		flagstr = flagstr $i @@ -101,17 +96,23 @@ active && /^[ \t]*\(/ {  		} else if (flagarray[arg] == "PTMP-ONLY") {  			flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "  		} else if (flagarray[arg] == "PASSIVE-SCAN") { -			flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " +			flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "  		} else if (flagarray[arg] == "NO-IBSS") { -			flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " +			flags = flags "\n\t\t\tNL80211_RRF_NO_IR | " +		} else if (flagarray[arg] == "NO-IR") { +			flags = flags "\n\t\t\tNL80211_RRF_NO_IR | " +		} else if (flagarray[arg] == "AUTO-BW") { +			flags = flags "\n\t\t\tNL80211_RRF_AUTO_BW | "  		} +  	}  	flags = flags "0" -	printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags +	printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %.0f, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags  	rules++  } -active && /^[ \t]*$/ { +function print_tail_country() +{  	active = 0  	printf "\t},\n"  	printf "\t.n_reg_rules = %d\n", rules @@ -119,7 +120,29 @@ active && /^[ \t]*$/ {  	rules = 0;  } +/^[ \t]*#/ { +	# Ignore +} + +!active && /^[ \t]*$/ { +	# Ignore +} + +!active && /country/ { +	parse_country_head() +} + +active && /^[ \t]*\(/ { +	parse_reg_rule() +} + +active && /^[ \t]*$/ { +	print_tail_country() +} +  END { +	if (active) +		print_tail_country()  	print regdb "};"  	print ""  	print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 39bff7d3676..8f345da3ea5 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -14,7 +14,8 @@  #include "rdev-ops.h" -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, +			    struct ieee80211_channel *channel)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_bss *bss; @@ -28,8 +29,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)  	if (!wdev->ssid_len)  		return; -	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, -			       wdev->ssid, wdev->ssid_len, +	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,  			       WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);  	if (WARN_ON(!bss)) @@ -45,7 +45,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)  	cfg80211_upload_connect_keys(wdev); -	nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, +	nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,  				GFP_KERNEL);  #ifdef CONFIG_CFG80211_WEXT  	memset(&wrqu, 0, sizeof(wrqu)); @@ -54,21 +54,26 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)  #endif  } -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, +			  struct ieee80211_channel *channel, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_event *ev;  	unsigned long flags; -	trace_cfg80211_ibss_joined(dev, bssid); +	trace_cfg80211_ibss_joined(dev, bssid, channel); + +	if (WARN_ON(!channel)) +		return;  	ev = kzalloc(sizeof(*ev), gfp);  	if (!ev)  		return;  	ev->type = EVENT_IBSS_JOINED; -	memcpy(ev->cr.bssid, bssid, ETH_ALEN); +	memcpy(ev->ij.bssid, bssid, ETH_ALEN); +	ev->ij.channel = channel;  	spin_lock_irqsave(&wdev->event_lock, flags);  	list_add_tail(&ev->list, &wdev->event_list); @@ -77,10 +82,10 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)  }  EXPORT_SYMBOL(cfg80211_ibss_joined); -int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, -			 struct net_device *dev, -			 struct cfg80211_ibss_params *params, -			 struct cfg80211_cached_keys *connkeys) +static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, +				struct net_device *dev, +				struct cfg80211_ibss_params *params, +				struct cfg80211_cached_keys *connkeys)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	int err; @@ -114,19 +119,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  	wdev->connect_keys = connkeys;  	wdev->ibss_fixed = params->channel_fixed; +	wdev->ibss_dfs_possible = params->userspace_handles_dfs; +	wdev->chandef = params->chandef;  #ifdef CONFIG_CFG80211_WEXT  	wdev->wext.ibss.chandef = params->chandef;  #endif - -	err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan, -				    params->channel_fixed -				    ? CHAN_MODE_SHARED -				    : CHAN_MODE_EXCLUSIVE); -	if (err) { -		wdev->connect_keys = NULL; -		return err; -	} -  	err = rdev_join_ibss(rdev, dev, params);  	if (err) {  		wdev->connect_keys = NULL; @@ -159,7 +156,7 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	int i;  	ASSERT_WDEV_LOCK(wdev); @@ -167,6 +164,8 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)  	kfree(wdev->connect_keys);  	wdev->connect_keys = NULL; +	rdev_set_qos_map(rdev, dev, NULL); +  	/*  	 * Delete all the keys ... pairwise keys can't really  	 * exist any more anyway, but default keys might. @@ -182,6 +181,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)  	wdev->current_bss = NULL;  	wdev->ssid_len = 0; +	memset(&wdev->chandef, 0, sizeof(wdev->chandef));  #ifdef CONFIG_CFG80211_WEXT  	if (!nowext)  		wdev->wext.ibss.ssid_len = 0; @@ -246,7 +246,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,  	/* try to find an IBSS channel if none requested ... */  	if (!wdev->wext.ibss.chandef.chan) { -		wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +		struct ieee80211_channel *new_chan = NULL;  		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {  			struct ieee80211_supported_band *sband; @@ -258,20 +258,23 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,  			for (i = 0; i < sband->n_channels; i++) {  				chan = &sband->channels[i]; -				if (chan->flags & IEEE80211_CHAN_NO_IBSS) +				if (chan->flags & IEEE80211_CHAN_NO_IR)  					continue;  				if (chan->flags & IEEE80211_CHAN_DISABLED)  					continue; -				wdev->wext.ibss.chandef.chan = chan; +				new_chan = chan;  				break;  			} -			if (wdev->wext.ibss.chandef.chan) +			if (new_chan)  				break;  		} -		if (!wdev->wext.ibss.chandef.chan) +		if (!new_chan)  			return -EINVAL; + +		cfg80211_chandef_create(&wdev->wext.ibss.chandef, new_chan, +					NL80211_CHAN_NO_HT);  	}  	/* don't join -- SSID is not there */ @@ -308,7 +311,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,  			       struct iw_freq *wextfreq, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct ieee80211_channel *chan = NULL;  	int err, freq; @@ -319,7 +322,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,  	if (!rdev->ops->join_ibss)  		return -EOPNOTSUPP; -	freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); +	freq = cfg80211_wext_freq(wextfreq);  	if (freq < 0)  		return freq; @@ -327,7 +330,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,  		chan = ieee80211_get_channel(wdev->wiphy, freq);  		if (!chan)  			return -EINVAL; -		if (chan->flags & IEEE80211_CHAN_NO_IBSS || +		if (chan->flags & IEEE80211_CHAN_NO_IR ||  		    chan->flags & IEEE80211_CHAN_DISABLED)  			return -EINVAL;  	} @@ -345,8 +348,8 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,  		return err;  	if (chan) { -		wdev->wext.ibss.chandef.chan = chan; -		wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; +		cfg80211_chandef_create(&wdev->wext.ibss.chandef, chan, +					NL80211_CHAN_NO_HT);  		wdev->wext.ibss.channel_fixed = true;  	} else {  		/* cfg80211_ibss_wext_join will pick one if needed */ @@ -393,7 +396,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,  				struct iw_point *data, char *ssid)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	size_t len = data->length;  	int err; @@ -417,8 +420,8 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,  	if (len > 0 && ssid[len - 1] == '\0')  		len--; +	memcpy(wdev->ssid, ssid, len);  	wdev->wext.ibss.ssid = wdev->ssid; -	memcpy(wdev->wext.ibss.ssid, ssid, len);  	wdev->wext.ibss.ssid_len = len;  	wdev_lock(wdev); @@ -460,7 +463,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,  			     struct sockaddr *ap_addr, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	u8 *bssid = ap_addr->sa_data;  	int err; @@ -478,6 +481,9 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,  	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))  		bssid = NULL; +	if (bssid && !is_valid_ether_addr(bssid)) +		return -EINVAL; +  	/* both automatic */  	if (!bssid && !wdev->wext.ibss.bssid)  		return 0; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0553fd4d85a..092300b30c3 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -141,8 +141,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,  			for (i = 0; i < sband->n_channels; i++) {  				chan = &sband->channels[i]; -				if (chan->flags & (IEEE80211_CHAN_NO_IBSS | -						   IEEE80211_CHAN_PASSIVE_SCAN | +				if (chan->flags & (IEEE80211_CHAN_NO_IR |  						   IEEE80211_CHAN_DISABLED |  						   IEEE80211_CHAN_RADAR))  					continue; @@ -175,19 +174,15 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,  							       scan_width);  	} -	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) +	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef, +				     NL80211_IFTYPE_MESH_POINT))  		return -EINVAL; -	err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan, -				    CHAN_MODE_SHARED); -	if (err) -		return err; -  	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->chandef.chan; +		wdev->chandef = setup->chandef;  	}  	return err; @@ -228,15 +223,10 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,  		if (!netif_running(wdev->netdev))  			return -ENETDOWN; -		err = cfg80211_can_use_chan(rdev, wdev, chandef->chan, -					    CHAN_MODE_SHARED); -		if (err) -			return err; -  		err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,  						     chandef->chan);  		if (!err) -			wdev->channel = chandef->chan; +			wdev->chandef = *chandef;  		return err;  	} @@ -248,8 +238,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,  	return 0;  } -static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, -				 struct net_device *dev) +int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, +			  struct net_device *dev)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	int err; @@ -268,7 +258,8 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,  	err = rdev_leave_mesh(rdev, dev);  	if (!err) {  		wdev->mesh_id_len = 0; -		wdev->channel = NULL; +		memset(&wdev->chandef, 0, sizeof(wdev->chandef)); +		rdev_set_qos_map(rdev, dev, NULL);  	}  	return err; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 8d49c1ce3de..266766b8d80 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -23,7 +23,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;  	u8 *ie = mgmt->u.assoc_resp.variable;  	int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); @@ -54,7 +54,7 @@ EXPORT_SYMBOL(cfg80211_rx_assoc_resp);  static void cfg80211_process_auth(struct wireless_dev *wdev,  				  const u8 *buf, size_t len)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);  	cfg80211_sme_rx_auth(wdev, buf, len); @@ -63,7 +63,7 @@ static void cfg80211_process_auth(struct wireless_dev *wdev,  static void cfg80211_process_deauth(struct wireless_dev *wdev,  				    const u8 *buf, size_t len)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;  	const u8 *bssid = mgmt->bssid;  	u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); @@ -82,7 +82,7 @@ static void cfg80211_process_deauth(struct wireless_dev *wdev,  static void cfg80211_process_disassoc(struct wireless_dev *wdev,  				      const u8 *buf, size_t len)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;  	const u8 *bssid = mgmt->bssid;  	u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); @@ -123,7 +123,7 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	trace_cfg80211_send_auth_timeout(dev, addr); @@ -136,7 +136,7 @@ void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	trace_cfg80211_send_assoc_timeout(dev, bss->bssid); @@ -172,7 +172,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,  				  const u8 *tsc, gfp_t gfp)  {  	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  #ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  	char *buf = kmalloc(128, gfp); @@ -233,14 +233,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,  	if (!req.bss)  		return -ENOENT; -	err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel, -				    CHAN_MODE_SHARED); -	if (err) -		goto out; -  	err = rdev_auth(rdev, dev, &req); -out:  	cfg80211_put_bss(&rdev->wiphy, req.bss);  	return err;  } @@ -306,16 +300,10 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,  	if (!req->bss)  		return -ENOENT; -	err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED); -	if (err) -		goto out; -  	err = rdev_assoc(rdev, dev, req);  	if (!err)  		cfg80211_hold_bss(bss_from_pub(req->bss)); - -out: -	if (err) +	else  		cfg80211_put_bss(&rdev->wiphy, req->bss);  	return err; @@ -414,7 +402,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,  				int match_len)  {  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct cfg80211_mgmt_registration *reg, *nreg;  	int err = 0;  	u16 mgmt_type; @@ -473,7 +461,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,  void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)  {  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct cfg80211_mgmt_registration *reg, *tmp;  	spin_lock_bh(&wdev->mgmt_registrations_lock); @@ -520,9 +508,7 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)  int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,  			  struct wireless_dev *wdev, -			  struct ieee80211_channel *chan, bool offchan, -			  unsigned int wait, const u8 *buf, size_t len, -			  bool no_cck, bool dont_wait_for_ack, u64 *cookie) +			  struct cfg80211_mgmt_tx_params *params, u64 *cookie)  {  	const struct ieee80211_mgmt *mgmt;  	u16 stype; @@ -533,10 +519,10 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,  	if (!rdev->ops->mgmt_tx)  		return -EOPNOTSUPP; -	if (len < 24 + 1) +	if (params->len < 24 + 1)  		return -EINVAL; -	mgmt = (const struct ieee80211_mgmt *) buf; +	mgmt = (const struct ieee80211_mgmt *)params->buf;  	if (!ieee80211_is_mgmt(mgmt->frame_control))  		return -EINVAL; @@ -615,16 +601,14 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,  		return -EINVAL;  	/* Transmit the Action frame as requested by user space */ -	return rdev_mgmt_tx(rdev, wdev, chan, offchan, -			    wait, buf, len, no_cck, dont_wait_for_ack, -			    cookie); +	return rdev_mgmt_tx(rdev, wdev, params, cookie);  }  bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,  		      const u8 *buf, size_t len, u32 flags, gfp_t gfp)  {  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct cfg80211_mgmt_registration *reg;  	const struct ieee80211_txrx_stypes *stypes =  		&wiphy->mgmt_stypes[wdev->iftype]; @@ -707,11 +691,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)  			if (c->dfs_state != NL80211_DFS_UNAVAILABLE)  				continue; -			timeout = c->dfs_state_entered + -				  IEEE80211_DFS_MIN_NOP_TIME_MS; +			timeout = c->dfs_state_entered + msecs_to_jiffies( +					IEEE80211_DFS_MIN_NOP_TIME_MS);  			if (time_after_eq(jiffies, timeout)) {  				c->dfs_state = NL80211_DFS_USABLE; +				c->dfs_state_entered = jiffies; +  				cfg80211_chandef_create(&chandef, c,  							NL80211_CHAN_NO_HT); @@ -741,7 +727,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,  			  struct cfg80211_chan_def *chandef,  			  gfp_t gfp)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	unsigned long timeout;  	trace_cfg80211_radar_event(wiphy, chandef); @@ -761,12 +747,12 @@ void cfg80211_radar_event(struct wiphy *wiphy,  EXPORT_SYMBOL(cfg80211_radar_event);  void cfg80211_cac_event(struct net_device *netdev, +			const struct cfg80211_chan_def *chandef,  			enum nl80211_radar_event event, gfp_t gfp)  {  	struct wireless_dev *wdev = netdev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); -	struct cfg80211_chan_def chandef; +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	unsigned long timeout;  	trace_cfg80211_cac_event(netdev, event); @@ -774,17 +760,15 @@ void cfg80211_cac_event(struct net_device *netdev,  	if (WARN_ON(!wdev->cac_started))  		return; -	if (WARN_ON(!wdev->channel)) +	if (WARN_ON(!wdev->chandef.chan))  		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); +			  msecs_to_jiffies(wdev->cac_time_ms);  		WARN_ON(!time_after_eq(jiffies, timeout)); -		cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE); +		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);  		break;  	case NL80211_RADAR_CAC_ABORTED:  		break; @@ -794,6 +778,6 @@ void cfg80211_cac_event(struct net_device *netdev,  	}  	wdev->cac_started = false; -	nl80211_radar_notify(rdev, &chandef, event, netdev, gfp); +	nl80211_radar_notify(rdev, chandef, event, netdev, gfp);  }  EXPORT_SYMBOL(cfg80211_cac_event); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index af8d84a4a5b..6668daf6932 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -30,9 +30,9 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,  				   struct cfg80211_crypto_settings *settings,  				   int cipher_limit); -static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, +static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,  			    struct genl_info *info); -static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, +static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,  			      struct genl_info *info);  /* the netlink family */ @@ -47,6 +47,27 @@ static struct genl_family nl80211_fam = {  	.post_doit = nl80211_post_doit,  }; +/* multicast groups */ +enum nl80211_multicast_groups { +	NL80211_MCGRP_CONFIG, +	NL80211_MCGRP_SCAN, +	NL80211_MCGRP_REGULATORY, +	NL80211_MCGRP_MLME, +	NL80211_MCGRP_VENDOR, +	NL80211_MCGRP_TESTMODE /* keep last - ifdef! */ +}; + +static const struct genl_multicast_group nl80211_mcgrps[] = { +	[NL80211_MCGRP_CONFIG] = { .name = "config", }, +	[NL80211_MCGRP_SCAN] = { .name = "scan", }, +	[NL80211_MCGRP_REGULATORY] = { .name = "regulatory", }, +	[NL80211_MCGRP_MLME] = { .name = "mlme", }, +	[NL80211_MCGRP_VENDOR] = { .name = "vendor", }, +#ifdef CONFIG_NL80211_TESTMODE +	[NL80211_MCGRP_TESTMODE] = { .name = "testmode", } +#endif +}; +  /* returns ERR_PTR values */  static struct wireless_dev *  __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs) @@ -144,16 +165,14 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)  	if (attrs[NL80211_ATTR_IFINDEX]) {  		int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); -		netdev = dev_get_by_index(netns, ifindex); +		netdev = __dev_get_by_index(netns, ifindex);  		if (netdev) {  			if (netdev->ieee80211_ptr) -				tmp = wiphy_to_dev( -						netdev->ieee80211_ptr->wiphy); +				tmp = wiphy_to_rdev( +					netdev->ieee80211_ptr->wiphy);  			else  				tmp = NULL; -			dev_put(netdev); -  			/* not wireless device -- return error */  			if (!tmp)  				return ERR_PTR(-EINVAL); @@ -352,8 +371,22 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {  	[NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },  	[NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },  	[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, -	[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 }, -	[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 }, +	[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_BINARY }, +	[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY }, +	[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY }, +	[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, +	[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, +	[NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, +	[NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, +	[NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, +	[NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, +	[NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY, +				   .len = IEEE80211_QOS_MAP_LEN_MAX }, +	[NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN }, +	[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, +	[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, +	[NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, +	[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },  };  /* policy for the key attributes */ @@ -453,7 +486,7 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,  			err = PTR_ERR(*wdev);  			goto out_unlock;  		} -		*rdev = wiphy_to_dev((*wdev)->wiphy); +		*rdev = wiphy_to_rdev((*wdev)->wiphy);  		/* 0 is the first index - add 1 to parse only once */  		cb->args[0] = (*rdev)->wiphy_idx + 1;  		cb->args[1] = (*wdev)->identifier; @@ -466,7 +499,7 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,  			err = -ENODEV;  			goto out_unlock;  		} -		*rdev = wiphy_to_dev(wiphy); +		*rdev = wiphy_to_rdev(wiphy);  		*wdev = NULL;  		list_for_each_entry(tmp, &(*rdev)->wdev_list, list) { @@ -535,6 +568,13 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,  				   struct ieee80211_channel *chan,  				   bool large)  { +	/* Some channels must be completely excluded from the +	 * list to protect old user-space tools from breaking +	 */ +	if (!large && chan->flags & +	    (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ)) +		return 0; +  	if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,  			chan->center_freq))  		goto nla_put_failure; @@ -542,12 +582,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,  	if ((chan->flags & IEEE80211_CHAN_DISABLED) &&  	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))  		goto nla_put_failure; -	if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) && -	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN)) -		goto nla_put_failure; -	if ((chan->flags & IEEE80211_CHAN_NO_IBSS) && -	    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS)) -		goto nla_put_failure; +	if (chan->flags & IEEE80211_CHAN_NO_IR) { +		if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR)) +			goto nla_put_failure; +		if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS)) +			goto nla_put_failure; +	}  	if (chan->flags & IEEE80211_CHAN_RADAR) {  		if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))  			goto nla_put_failure; @@ -562,6 +602,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,  			if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,  					time))  				goto nla_put_failure; +			if (nla_put_u32(msg, +					NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, +					chan->dfs_cac_ms)) +				goto nla_put_failure;  		}  	} @@ -578,6 +622,18 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,  		if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&  		    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))  			goto nla_put_failure; +		if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) && +		    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_INDOOR_ONLY)) +			goto nla_put_failure; +		if ((chan->flags & IEEE80211_CHAN_GO_CONCURRENT) && +		    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_GO_CONCURRENT)) +			goto nla_put_failure; +		if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) && +		    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ)) +			goto nla_put_failure; +		if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) && +		    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ)) +			goto nla_put_failure;  	}  	if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, @@ -827,6 +883,19 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)  	return 0;  } +static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy, +							struct nlattr *tb) +{ +	struct ieee80211_channel *chan; + +	if (tb == NULL) +		return NULL; +	chan = ieee80211_get_channel(wiphy, nla_get_u32(tb)); +	if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) +		return NULL; +	return chan; +} +  static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)  {  	struct nlattr *nl_modes = nla_nest_start(msg, attr); @@ -902,8 +971,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,  				c->max_interfaces))  			goto nla_put_failure;  		if (large && -		    nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, -				c->radar_detect_widths)) +		    (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, +				c->radar_detect_widths) || +		     nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, +				c->radar_detect_regions)))  			goto nla_put_failure;  		nla_nest_end(msg, nl_combi); @@ -958,42 +1029,42 @@ static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,  }  static int nl80211_send_wowlan(struct sk_buff *msg, -			       struct cfg80211_registered_device *dev, +			       struct cfg80211_registered_device *rdev,  			       bool large)  {  	struct nlattr *nl_wowlan; -	if (!dev->wiphy.wowlan) +	if (!rdev->wiphy.wowlan)  		return 0;  	nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);  	if (!nl_wowlan)  		return -ENOBUFS; -	if (((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) && +	if (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || -	    ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) && +	    ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || -	    ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) && +	    ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || -	    ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) && +	    ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) || -	    ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && +	    ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || -	    ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) && +	    ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || -	    ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) && +	    ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || -	    ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) && +	    ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&  	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))  		return -ENOBUFS; -	if (dev->wiphy.wowlan->n_patterns) { +	if (rdev->wiphy.wowlan->n_patterns) {  		struct nl80211_pattern_support pat = { -			.max_patterns = dev->wiphy.wowlan->n_patterns, -			.min_pattern_len = dev->wiphy.wowlan->pattern_min_len, -			.max_pattern_len = dev->wiphy.wowlan->pattern_max_len, -			.max_pkt_offset = dev->wiphy.wowlan->max_pkt_offset, +			.max_patterns = rdev->wiphy.wowlan->n_patterns, +			.min_pattern_len = rdev->wiphy.wowlan->pattern_min_len, +			.max_pattern_len = rdev->wiphy.wowlan->pattern_max_len, +			.max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset,  		};  		if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, @@ -1001,7 +1072,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg,  			return -ENOBUFS;  	} -	if (large && nl80211_send_wowlan_tcp_caps(dev, msg)) +	if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))  		return -ENOBUFS;  	nla_nest_end(msg, nl_wowlan); @@ -1011,19 +1082,19 @@ static int nl80211_send_wowlan(struct sk_buff *msg,  #endif  static int nl80211_send_coalesce(struct sk_buff *msg, -				 struct cfg80211_registered_device *dev) +				 struct cfg80211_registered_device *rdev)  {  	struct nl80211_coalesce_rule_support rule; -	if (!dev->wiphy.coalesce) +	if (!rdev->wiphy.coalesce)  		return 0; -	rule.max_rules = dev->wiphy.coalesce->n_rules; -	rule.max_delay = dev->wiphy.coalesce->max_delay; -	rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns; -	rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len; -	rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len; -	rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset; +	rule.max_rules = rdev->wiphy.coalesce->n_rules; +	rule.max_delay = rdev->wiphy.coalesce->max_delay; +	rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns; +	rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len; +	rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len; +	rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset;  	if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))  		return -ENOBUFS; @@ -1154,7 +1225,8 @@ struct nl80211_dump_wiphy_state {  	bool split;  }; -static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, +static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, +			      enum nl80211_commands cmd,  			      struct sk_buff *msg, u32 portid, u32 seq,  			      int flags, struct nl80211_dump_wiphy_state *state)  { @@ -1166,103 +1238,102 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  	struct ieee80211_channel *chan;  	int i;  	const struct ieee80211_txrx_stypes *mgmt_stypes = -				dev->wiphy.mgmt_stypes; +				rdev->wiphy.mgmt_stypes;  	u32 features; -	hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); +	hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);  	if (!hdr)  		return -ENOBUFS;  	if (WARN_ON(!state))  		return -EINVAL; -	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) || +	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||  	    nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, -			   wiphy_name(&dev->wiphy)) || +			   wiphy_name(&rdev->wiphy)) ||  	    nla_put_u32(msg, NL80211_ATTR_GENERATION,  			cfg80211_rdev_list_generation))  		goto nla_put_failure; +	if (cmd != NL80211_CMD_NEW_WIPHY) +		goto finish; +  	switch (state->split_start) {  	case 0:  		if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, -			       dev->wiphy.retry_short) || +			       rdev->wiphy.retry_short) ||  		    nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, -			       dev->wiphy.retry_long) || +			       rdev->wiphy.retry_long) ||  		    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, -				dev->wiphy.frag_threshold) || +				rdev->wiphy.frag_threshold) ||  		    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, -				dev->wiphy.rts_threshold) || +				rdev->wiphy.rts_threshold) ||  		    nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, -			       dev->wiphy.coverage_class) || +			       rdev->wiphy.coverage_class) ||  		    nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, -			       dev->wiphy.max_scan_ssids) || +			       rdev->wiphy.max_scan_ssids) ||  		    nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, -			       dev->wiphy.max_sched_scan_ssids) || +			       rdev->wiphy.max_sched_scan_ssids) ||  		    nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, -				dev->wiphy.max_scan_ie_len) || +				rdev->wiphy.max_scan_ie_len) ||  		    nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, -				dev->wiphy.max_sched_scan_ie_len) || +				rdev->wiphy.max_sched_scan_ie_len) ||  		    nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS, -			       dev->wiphy.max_match_sets)) +			       rdev->wiphy.max_match_sets))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&  		    nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&  		    nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&  		    nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&  		    nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&  		    nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&  		    nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && -		    nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ)) -			goto nla_put_failure; -  		state->split_start++;  		if (state->split)  			break;  	case 1:  		if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES, -			    sizeof(u32) * dev->wiphy.n_cipher_suites, -			    dev->wiphy.cipher_suites)) +			    sizeof(u32) * rdev->wiphy.n_cipher_suites, +			    rdev->wiphy.cipher_suites))  			goto nla_put_failure;  		if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, -			       dev->wiphy.max_num_pmkids)) +			       rdev->wiphy.max_num_pmkids))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&  		    nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))  			goto nla_put_failure;  		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, -				dev->wiphy.available_antennas_tx) || +				rdev->wiphy.available_antennas_tx) ||  		    nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, -				dev->wiphy.available_antennas_rx)) +				rdev->wiphy.available_antennas_rx))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&  		    nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD, -				dev->wiphy.probe_resp_offload)) +				rdev->wiphy.probe_resp_offload))  			goto nla_put_failure; -		if ((dev->wiphy.available_antennas_tx || -		     dev->wiphy.available_antennas_rx) && -		    dev->ops->get_antenna) { +		if ((rdev->wiphy.available_antennas_tx || +		     rdev->wiphy.available_antennas_rx) && +		    rdev->ops->get_antenna) {  			u32 tx_ant = 0, rx_ant = 0;  			int res; -			res = rdev_get_antenna(dev, &tx_ant, &rx_ant); +			res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);  			if (!res) {  				if (nla_put_u32(msg,  						NL80211_ATTR_WIPHY_ANTENNA_TX, @@ -1279,7 +1350,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  			break;  	case 2:  		if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, -					dev->wiphy.interface_modes)) +					rdev->wiphy.interface_modes))  				goto nla_put_failure;  		state->split_start++;  		if (state->split) @@ -1293,7 +1364,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  		     band < IEEE80211_NUM_BANDS; band++) {  			struct ieee80211_supported_band *sband; -			sband = dev->wiphy.bands[band]; +			sband = rdev->wiphy.bands[band];  			if (!sband)  				continue; @@ -1370,7 +1441,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  		i = 0;  #define CMD(op, n)							\  		 do {							\ -			if (dev->ops->op) {				\ +			if (rdev->ops->op) {				\  				i++;					\  				if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \  					goto nla_put_failure;		\ @@ -1394,58 +1465,58 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  		CMD(set_pmksa, SET_PMKSA);  		CMD(del_pmksa, DEL_PMKSA);  		CMD(flush_pmksa, FLUSH_PMKSA); -		if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) +		if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)  			CMD(remain_on_channel, REMAIN_ON_CHANNEL);  		CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);  		CMD(mgmt_tx, FRAME);  		CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); -		if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { +		if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {  			i++;  			if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))  				goto nla_put_failure;  		} -		if (dev->ops->set_monitor_channel || dev->ops->start_ap || -		    dev->ops->join_mesh) { +		if (rdev->ops->set_monitor_channel || rdev->ops->start_ap || +		    rdev->ops->join_mesh) {  			i++;  			if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))  				goto nla_put_failure;  		}  		CMD(set_wds_peer, SET_WDS_PEER); -		if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { +		if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {  			CMD(tdls_mgmt, TDLS_MGMT);  			CMD(tdls_oper, TDLS_OPER);  		} -		if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) +		if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)  			CMD(sched_scan_start, START_SCHED_SCAN);  		CMD(probe_client, PROBE_CLIENT);  		CMD(set_noack_map, SET_NOACK_MAP); -		if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { +		if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {  			i++;  			if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))  				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); +#endif  		if (state->split) {  			CMD(crit_proto_start, CRIT_PROTOCOL_START);  			CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); -			if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) +			if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)  				CMD(channel_switch, CHANNEL_SWITCH); +			CMD(set_qos_map, SET_QOS_MAP);  		} - -#ifdef CONFIG_NL80211_TESTMODE -		CMD(testmode_cmd, TESTMODE); -#endif - +		/* add into the if now */  #undef CMD -		if (dev->ops->connect || dev->ops->auth) { +		if (rdev->ops->connect || rdev->ops->auth) {  			i++;  			if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))  				goto nla_put_failure;  		} -		if (dev->ops->disconnect || dev->ops->deauth) { +		if (rdev->ops->disconnect || rdev->ops->deauth) {  			i++;  			if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))  				goto nla_put_failure; @@ -1456,14 +1527,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  		if (state->split)  			break;  	case 5: -		if (dev->ops->remain_on_channel && -		    (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && +		if (rdev->ops->remain_on_channel && +		    (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&  		    nla_put_u32(msg,  				NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, -				dev->wiphy.max_remain_on_channel_duration)) +				rdev->wiphy.max_remain_on_channel_duration))  			goto nla_put_failure; -		if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&  		    nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))  			goto nla_put_failure; @@ -1474,7 +1545,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  			break;  	case 6:  #ifdef CONFIG_PM -		if (nl80211_send_wowlan(msg, dev, state->split)) +		if (nl80211_send_wowlan(msg, rdev, state->split))  			goto nla_put_failure;  		state->split_start++;  		if (state->split) @@ -1484,10 +1555,10 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  #endif  	case 7:  		if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, -					dev->wiphy.software_iftypes)) +					rdev->wiphy.software_iftypes))  			goto nla_put_failure; -		if (nl80211_put_iface_combinations(&dev->wiphy, msg, +		if (nl80211_put_iface_combinations(&rdev->wiphy, msg,  						   state->split))  			goto nla_put_failure; @@ -1495,12 +1566,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  		if (state->split)  			break;  	case 8: -		if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && +		if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&  		    nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME, -				dev->wiphy.ap_sme_capa)) +				rdev->wiphy.ap_sme_capa))  			goto nla_put_failure; -		features = dev->wiphy.features; +		features = rdev->wiphy.features;  		/*  		 * We can only add the per-channel limit information if the  		 * dump is split, otherwise it makes it too big. Therefore @@ -1511,16 +1582,16 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  		if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))  			goto nla_put_failure; -		if (dev->wiphy.ht_capa_mod_mask && +		if (rdev->wiphy.ht_capa_mod_mask &&  		    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, -			    sizeof(*dev->wiphy.ht_capa_mod_mask), -			    dev->wiphy.ht_capa_mod_mask)) +			    sizeof(*rdev->wiphy.ht_capa_mod_mask), +			    rdev->wiphy.ht_capa_mod_mask))  			goto nla_put_failure; -		if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && -		    dev->wiphy.max_acl_mac_addrs && +		if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && +		    rdev->wiphy.max_acl_mac_addrs &&  		    nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX, -				dev->wiphy.max_acl_mac_addrs)) +				rdev->wiphy.max_acl_mac_addrs))  			goto nla_put_failure;  		/* @@ -1536,31 +1607,85 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,  		state->split_start++;  		break;  	case 9: -		if (dev->wiphy.extended_capabilities && +		if (rdev->wiphy.extended_capabilities &&  		    (nla_put(msg, NL80211_ATTR_EXT_CAPA, -			     dev->wiphy.extended_capabilities_len, -			     dev->wiphy.extended_capabilities) || +			     rdev->wiphy.extended_capabilities_len, +			     rdev->wiphy.extended_capabilities) ||  		     nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK, -			     dev->wiphy.extended_capabilities_len, -			     dev->wiphy.extended_capabilities_mask))) +			     rdev->wiphy.extended_capabilities_len, +			     rdev->wiphy.extended_capabilities_mask)))  			goto nla_put_failure; -		if (dev->wiphy.vht_capa_mod_mask && +		if (rdev->wiphy.vht_capa_mod_mask &&  		    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, -			    sizeof(*dev->wiphy.vht_capa_mod_mask), -			    dev->wiphy.vht_capa_mod_mask)) +			    sizeof(*rdev->wiphy.vht_capa_mod_mask), +			    rdev->wiphy.vht_capa_mod_mask))  			goto nla_put_failure;  		state->split_start++;  		break;  	case 10: -		if (nl80211_send_coalesce(msg, dev)) +		if (nl80211_send_coalesce(msg, rdev)) +			goto nla_put_failure; + +		if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && +		    (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || +		     nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) +			goto nla_put_failure; + +		if (rdev->wiphy.max_ap_assoc_sta && +		    nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA, +				rdev->wiphy.max_ap_assoc_sta)) +			goto nla_put_failure; + +		state->split_start++; +		break; +	case 11: +		if (rdev->wiphy.n_vendor_commands) { +			const struct nl80211_vendor_cmd_info *info; +			struct nlattr *nested; + +			nested = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); +			if (!nested) +				goto nla_put_failure; + +			for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { +				info = &rdev->wiphy.vendor_commands[i].info; +				if (nla_put(msg, i + 1, sizeof(*info), info)) +					goto nla_put_failure; +			} +			nla_nest_end(msg, nested); +		} + +		if (rdev->wiphy.n_vendor_events) { +			const struct nl80211_vendor_cmd_info *info; +			struct nlattr *nested; + +			nested = nla_nest_start(msg, +						NL80211_ATTR_VENDOR_EVENTS); +			if (!nested) +				goto nla_put_failure; + +			for (i = 0; i < rdev->wiphy.n_vendor_events; i++) { +				info = &rdev->wiphy.vendor_events[i]; +				if (nla_put(msg, i + 1, sizeof(*info), info)) +					goto nla_put_failure; +			} +			nla_nest_end(msg, nested); +		} +		state->split_start++; +		break; +	case 12: +		if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH && +		    nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS, +			       rdev->wiphy.max_num_csa_counters))  			goto nla_put_failure;  		/* done */  		state->split_start = 0;  		break;  	} + finish:  	return genlmsg_end(msg, hdr);   nla_put_failure: @@ -1589,15 +1714,14 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,  		struct cfg80211_registered_device *rdev;  		int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); -		netdev = dev_get_by_index(sock_net(skb->sk), ifidx); +		netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);  		if (!netdev)  			return -ENODEV;  		if (netdev->ieee80211_ptr) { -			rdev = wiphy_to_dev( +			rdev = wiphy_to_rdev(  				netdev->ieee80211_ptr->wiphy);  			state->filter_wiphy = rdev->wiphy_idx;  		} -		dev_put(netdev);  	}  	return 0; @@ -1607,7 +1731,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)  {  	int idx = 0, ret;  	struct nl80211_dump_wiphy_state *state = (void *)cb->args[0]; -	struct cfg80211_registered_device *dev; +	struct cfg80211_registered_device *rdev;  	rtnl_lock();  	if (!state) { @@ -1626,17 +1750,18 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)  		cb->args[0] = (long)state;  	} -	list_for_each_entry(dev, &cfg80211_rdev_list, list) { -		if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) +	list_for_each_entry(rdev, &cfg80211_rdev_list, list) { +		if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))  			continue;  		if (++idx <= state->start)  			continue;  		if (state->filter_wiphy != -1 && -		    state->filter_wiphy != dev->wiphy_idx) +		    state->filter_wiphy != rdev->wiphy_idx)  			continue;  		/* attempt to fit multiple wiphy data chunks into the skb */  		do { -			ret = nl80211_send_wiphy(dev, skb, +			ret = nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, +						 skb,  						 NETLINK_CB(cb->skb).portid,  						 cb->nlh->nlmsg_seq,  						 NLM_F_MULTI, state); @@ -1655,9 +1780,10 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)  				 * We can then retry with the larger buffer.  				 */  				if ((ret == -ENOBUFS || ret == -EMSGSIZE) && -				    !skb->len && +				    !skb->len && !state->split &&  				    cb->min_dump_alloc < 4096) {  					cb->min_dump_alloc = 4096; +					state->split_start = 0;  					rtnl_unlock();  					return 1;  				} @@ -1683,14 +1809,15 @@ static int nl80211_dump_wiphy_done(struct netlink_callback *cb)  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]; +	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct nl80211_dump_wiphy_state state = {};  	msg = nlmsg_new(4096, GFP_KERNEL);  	if (!msg)  		return -ENOMEM; -	if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0, +	if (nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, msg, +			       info->snd_portid, info->snd_seq, 0,  			       &state) < 0) {  		nlmsg_free(msg);  		return -ENOBUFS; @@ -1817,18 +1944,20 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,  }  static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, -				 struct wireless_dev *wdev, +				 struct net_device *dev,  				 struct genl_info *info)  {  	struct cfg80211_chan_def chandef;  	int result;  	enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; +	struct wireless_dev *wdev = NULL; -	if (wdev) -		iftype = wdev->iftype; - +	if (dev) +		wdev = dev->ieee80211_ptr;  	if (!nl80211_can_set_dev_channel(wdev))  		return -EOPNOTSUPP; +	if (wdev) +		iftype = wdev->iftype;  	result = nl80211_parse_chandef(rdev, info, &chandef);  	if (result) @@ -1837,14 +1966,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,  	switch (iftype) {  	case NL80211_IFTYPE_AP:  	case NL80211_IFTYPE_P2P_GO: -		if (wdev->beacon_interval) { -			result = -EBUSY; -			break; -		} -		if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) { +		if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {  			result = -EINVAL;  			break;  		} +		if (wdev->beacon_interval) { +			if (!dev || !rdev->ops->set_ap_chanwidth || +			    !(rdev->wiphy.features & +			      NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) { +				result = -EBUSY; +				break; +			} + +			/* Only allow dynamic channel width changes */ +			if (chandef.chan != wdev->preset_chandef.chan) { +				result = -EBUSY; +				break; +			} +			result = rdev_set_ap_chanwidth(rdev, dev, &chandef); +			if (result) +				break; +		}  		wdev->preset_chandef = chandef;  		result = 0;  		break; @@ -1866,7 +2008,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct net_device *netdev = info->user_ptr[1]; -	return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); +	return __nl80211_set_channel(rdev, netdev, info);  }  static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info) @@ -1920,9 +2062,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_IFINDEX]) {  		int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); -		netdev = dev_get_by_index(genl_info_net(info), ifindex); +		netdev = __dev_get_by_index(genl_info_net(info), ifindex);  		if (netdev && netdev->ieee80211_ptr) -			rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); +			rdev = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);  		else  			netdev = NULL;  	} @@ -1948,57 +2090,52 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  			rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));  	if (result) -		goto bad_res; +		return result;  	if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {  		struct ieee80211_txq_params txq_params;  		struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; -		if (!rdev->ops->set_txq_params) { -			result = -EOPNOTSUPP; -			goto bad_res; -		} +		if (!rdev->ops->set_txq_params) +			return -EOPNOTSUPP; -		if (!netdev) { -			result = -EINVAL; -			goto bad_res; -		} +		if (!netdev) +			return -EINVAL;  		if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && -		    netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { -			result = -EINVAL; -			goto bad_res; -		} +		    netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) +			return -EINVAL; -		if (!netif_running(netdev)) { -			result = -ENETDOWN; -			goto bad_res; -		} +		if (!netif_running(netdev)) +			return -ENETDOWN;  		nla_for_each_nested(nl_txq_params,  				    info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],  				    rem_txq_params) { -			nla_parse(tb, NL80211_TXQ_ATTR_MAX, -				  nla_data(nl_txq_params), -				  nla_len(nl_txq_params), -				  txq_params_policy); +			result = nla_parse(tb, NL80211_TXQ_ATTR_MAX, +					   nla_data(nl_txq_params), +					   nla_len(nl_txq_params), +					   txq_params_policy); +			if (result) +				return result;  			result = parse_txq_params(tb, &txq_params);  			if (result) -				goto bad_res; +				return result;  			result = rdev_set_txq_params(rdev, netdev,  						     &txq_params);  			if (result) -				goto bad_res; +				return result;  		}  	}  	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { -		result = __nl80211_set_channel(rdev, -				nl80211_can_set_dev_channel(wdev) ? wdev : NULL, -				info); +		result = __nl80211_set_channel( +			rdev, +			nl80211_can_set_dev_channel(wdev) ? netdev : NULL, +			info);  		if (result) -			goto bad_res; +			return result;  	}  	if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { @@ -2009,19 +2146,15 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))  			txp_wdev = NULL; -		if (!rdev->ops->set_tx_power) { -			result = -EOPNOTSUPP; -			goto bad_res; -		} +		if (!rdev->ops->set_tx_power) +			return -EOPNOTSUPP;  		idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;  		type = nla_get_u32(info->attrs[idx]);  		if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] && -		    (type != NL80211_TX_POWER_AUTOMATIC)) { -			result = -EINVAL; -			goto bad_res; -		} +		    (type != NL80211_TX_POWER_AUTOMATIC)) +			return -EINVAL;  		if (type != NL80211_TX_POWER_AUTOMATIC) {  			idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL; @@ -2030,7 +2163,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);  		if (result) -			goto bad_res; +			return result;  	}  	if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && @@ -2038,10 +2171,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		u32 tx_ant, rx_ant;  		if ((!rdev->wiphy.available_antennas_tx &&  		     !rdev->wiphy.available_antennas_rx) || -		    !rdev->ops->set_antenna) { -			result = -EOPNOTSUPP; -			goto bad_res; -		} +		    !rdev->ops->set_antenna) +			return -EOPNOTSUPP;  		tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);  		rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); @@ -2049,17 +2180,15 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		/* reject antenna configurations which don't match the  		 * available antenna masks, except for the "all" mask */  		if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || -		    (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) { -			result = -EINVAL; -			goto bad_res; -		} +		    (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) +			return -EINVAL;  		tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;  		rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;  		result = rdev_set_antenna(rdev, tx_ant, rx_ant);  		if (result) -			goto bad_res; +			return result;  	}  	changed = 0; @@ -2067,30 +2196,27 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {  		retry_short = nla_get_u8(  			info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]); -		if (retry_short == 0) { -			result = -EINVAL; -			goto bad_res; -		} +		if (retry_short == 0) +			return -EINVAL; +  		changed |= WIPHY_PARAM_RETRY_SHORT;  	}  	if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {  		retry_long = nla_get_u8(  			info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]); -		if (retry_long == 0) { -			result = -EINVAL; -			goto bad_res; -		} +		if (retry_long == 0) +			return -EINVAL; +  		changed |= WIPHY_PARAM_RETRY_LONG;  	}  	if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {  		frag_threshold = nla_get_u32(  			info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); -		if (frag_threshold < 256) { -			result = -EINVAL; -			goto bad_res; -		} +		if (frag_threshold < 256) +			return -EINVAL; +  		if (frag_threshold != (u32) -1) {  			/*  			 * Fragments (apart from the last one) are required to @@ -2120,10 +2246,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  		u32 old_frag_threshold, old_rts_threshold;  		u8 old_coverage_class; -		if (!rdev->ops->set_wiphy_params) { -			result = -EOPNOTSUPP; -			goto bad_res; -		} +		if (!rdev->ops->set_wiphy_params) +			return -EOPNOTSUPP;  		old_retry_short = rdev->wiphy.retry_short;  		old_retry_long = rdev->wiphy.retry_long; @@ -2151,21 +2275,17 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)  			rdev->wiphy.coverage_class = old_coverage_class;  		}  	} - - bad_res: -	if (netdev) -		dev_put(netdev); -	return result; +	return 0;  }  static inline u64 wdev_id(struct wireless_dev *wdev)  {  	return (u64)wdev->identifier | -	       ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32); +	       ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);  }  static int nl80211_send_chandef(struct sk_buff *msg, -				 struct cfg80211_chan_def *chandef) +				const struct cfg80211_chan_def *chandef)  {  	WARN_ON(!cfg80211_chandef_valid(chandef)); @@ -2287,7 +2407,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *  static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)  {  	struct sk_buff *msg; -	struct cfg80211_registered_device *dev = info->user_ptr[0]; +	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct wireless_dev *wdev = info->user_ptr[1];  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -2295,7 +2415,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)  		return -ENOMEM;  	if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, -			       dev, wdev) < 0) { +			       rdev, wdev) < 0) {  		nlmsg_free(msg);  		return -ENOBUFS;  	} @@ -2421,7 +2541,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)  		change = true;  	} -	if (flags && (*flags & NL80211_MNTR_FLAG_ACTIVE) && +	if (flags && (*flags & MONITOR_FLAG_ACTIVE) &&  	    !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))  		return -EOPNOTSUPP; @@ -2446,6 +2566,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)  	enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;  	u32 flags; +	/* to avoid failing a new interface creation due to pending removal */ +	cfg80211_destroy_ifaces(rdev); +  	memset(¶ms, 0, sizeof(params));  	if (!info->attrs[NL80211_ATTR_IFNAME]) @@ -2483,7 +2606,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)  				  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,  				  &flags); -	if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) && +	if (!err && (flags & MONITOR_FLAG_ACTIVE) &&  	    !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))  		return -EOPNOTSUPP; @@ -2495,6 +2618,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)  		return PTR_ERR(wdev);  	} +	if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER]) +		wdev->owner_nlportid = info->snd_portid; +  	switch (type) {  	case NL80211_IFTYPE_MESH_POINT:  		if (!info->attrs[NL80211_ATTR_MESH_ID]) @@ -2665,7 +2791,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)  	hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,  			     NL80211_CMD_NEW_KEY);  	if (!hdr) -		return -ENOBUFS; +		goto nla_put_failure;  	cookie.msg = msg;  	cookie.idx = key_idx; @@ -3074,7 +3200,6 @@ 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) @@ -3190,38 +3315,26 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)  	} else if (!nl80211_get_ap_channel(rdev, ¶ms))  		return -EINVAL; -	if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) +	if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef, +				     wdev->iftype))  		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; -	} - -	err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, -					   params.chandef.chan, -					   CHAN_MODE_SHARED, -					   radar_detect_width); -	if (err) -		return err; -  	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);  	} +	wdev_lock(wdev);  	err = rdev_start_ap(rdev, dev, ¶ms);  	if (!err) {  		wdev->preset_chandef = params.chandef;  		wdev->beacon_interval = params.beacon_interval; -		wdev->channel = params.chandef.chan; +		wdev->chandef = params.chandef;  		wdev->ssid_len = params.ssid_len;  		memcpy(wdev->ssid, params.ssid, wdev->ssid_len);  	} +	wdev_unlock(wdev);  	kfree(params.acl); @@ -3250,7 +3363,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)  	if (err)  		return err; -	return rdev_change_beacon(rdev, dev, ¶ms); +	wdev_lock(wdev); +	err = rdev_change_beacon(rdev, dev, ¶ms); +	wdev_unlock(wdev); + +	return err;  }  static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) @@ -3258,7 +3375,7 @@ static int nl80211_stop_ap(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]; -	return cfg80211_stop_ap(rdev, dev); +	return cfg80211_stop_ap(rdev, dev, false);  }  static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { @@ -3539,6 +3656,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,  	    nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,  			sinfo->tx_failed))  		goto nla_put_failure; +	if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) && +	    nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT, +			sinfo->expected_throughput)) +		goto nla_put_failure;  	if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&  	    nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,  			sinfo->beacon_loss_count)) @@ -3601,13 +3722,13 @@ static int nl80211_dump_station(struct sk_buff *skb,  				struct netlink_callback *cb)  {  	struct station_info sinfo; -	struct cfg80211_registered_device *dev; +	struct cfg80211_registered_device *rdev;  	struct wireless_dev *wdev;  	u8 mac_addr[ETH_ALEN];  	int sta_idx = cb->args[2];  	int err; -	err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev); +	err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);  	if (err)  		return err; @@ -3616,14 +3737,14 @@ static int nl80211_dump_station(struct sk_buff *skb,  		goto out_err;  	} -	if (!dev->ops->dump_station) { +	if (!rdev->ops->dump_station) {  		err = -EOPNOTSUPP;  		goto out_err;  	}  	while (1) {  		memset(&sinfo, 0, sizeof(sinfo)); -		err = rdev_dump_station(dev, wdev->netdev, sta_idx, +		err = rdev_dump_station(rdev, wdev->netdev, sta_idx,  					mac_addr, &sinfo);  		if (err == -ENOENT)  			break; @@ -3633,7 +3754,7 @@ static int nl80211_dump_station(struct sk_buff *skb,  		if (nl80211_send_station(skb,  				NETLINK_CB(cb->skb).portid,  				cb->nlh->nlmsg_seq, NLM_F_MULTI, -				dev, wdev->netdev, mac_addr, +				rdev, wdev->netdev, mac_addr,  				&sinfo) < 0)  			goto out; @@ -3645,7 +3766,7 @@ static int nl80211_dump_station(struct sk_buff *skb,  	cb->args[2] = sta_idx;  	err = skb->len;   out_err: -	nl80211_finish_wdev_dump(dev); +	nl80211_finish_wdev_dump(rdev);  	return err;  } @@ -3856,8 +3977,8 @@ 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 = { +static const struct nla_policy +nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {  	[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },  	[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },  }; @@ -3896,9 +4017,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info,  	return 0;  } +static int nl80211_parse_sta_channel_info(struct genl_info *info, +				      struct station_parameters *params) +{ +	if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) { +		params->supported_channels = +		     nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); +		params->supported_channels_len = +		     nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); +		/* +		 * Need to include at least one (first channel, number of +		 * channels) tuple for each subband, and must have proper +		 * tuples for the rest of the data as well. +		 */ +		if (params->supported_channels_len < 2) +			return -EINVAL; +		if (params->supported_channels_len % 2) +			return -EINVAL; +	} + +	if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) { +		params->supported_oper_classes = +		 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); +		params->supported_oper_classes_len = +		  nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); +		/* +		 * The value of the Length field of the Supported Operating +		 * Classes element is between 2 and 253. +		 */ +		if (params->supported_oper_classes_len < 2 || +		    params->supported_oper_classes_len > 253) +			return -EINVAL; +	} +	return 0; +} +  static int nl80211_set_station_tdls(struct genl_info *info,  				    struct station_parameters *params)  { +	int err;  	/* Dummy STA entry gets updated once the peer capabilities are known */  	if (info->attrs[NL80211_ATTR_PEER_AID])  		params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); @@ -3909,6 +4066,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,  		params->vht_capa =  			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); +	err = nl80211_parse_sta_channel_info(info, params); +	if (err) +		return err; +  	return nl80211_parse_sta_wme(info, params);  } @@ -4082,6 +4243,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)  		params.vht_capa =  			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); +	if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { +		params.opmode_notif_used = true; +		params.opmode_notif = +			nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]); +	} +  	if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {  		params.plink_action =  			nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); @@ -4089,6 +4256,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)  			return -EINVAL;  	} +	err = nl80211_parse_sta_channel_info(info, ¶ms); +	if (err) +		return err; +  	err = nl80211_parse_sta_wme(info, ¶ms);  	if (err)  		return err; @@ -4256,18 +4427,18 @@ static int nl80211_dump_mpath(struct sk_buff *skb,  			      struct netlink_callback *cb)  {  	struct mpath_info pinfo; -	struct cfg80211_registered_device *dev; +	struct cfg80211_registered_device *rdev;  	struct wireless_dev *wdev;  	u8 dst[ETH_ALEN];  	u8 next_hop[ETH_ALEN];  	int path_idx = cb->args[2];  	int err; -	err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev); +	err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);  	if (err)  		return err; -	if (!dev->ops->dump_mpath) { +	if (!rdev->ops->dump_mpath) {  		err = -EOPNOTSUPP;  		goto out_err;  	} @@ -4278,7 +4449,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,  	}  	while (1) { -		err = rdev_dump_mpath(dev, wdev->netdev, path_idx, dst, +		err = rdev_dump_mpath(rdev, wdev->netdev, path_idx, dst,  				      next_hop, &pinfo);  		if (err == -ENOENT)  			break; @@ -4299,7 +4470,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,  	cb->args[2] = path_idx;  	err = skb->len;   out_err: -	nl80211_finish_wdev_dump(dev); +	nl80211_finish_wdev_dump(rdev);  	return err;  } @@ -4412,7 +4583,9 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0];  	struct net_device *dev = info->user_ptr[1]; +	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct bss_parameters params; +	int err;  	memset(¶ms, 0, sizeof(params));  	/* default to not changing parameters */ @@ -4478,7 +4651,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)  	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)  		return -EOPNOTSUPP; -	return rdev_change_bss(rdev, dev, ¶ms); +	wdev_lock(wdev); +	err = rdev_change_bss(rdev, dev, ¶ms); +	wdev_unlock(wdev); + +	return err;  }  static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { @@ -4488,6 +4665,7 @@ static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] =  	[NL80211_ATTR_FREQ_RANGE_MAX_BW]	= { .type = NLA_U32 },  	[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]	= { .type = NLA_U32 },  	[NL80211_ATTR_POWER_RULE_MAX_EIRP]	= { .type = NLA_U32 }, +	[NL80211_ATTR_DFS_CAC_TIME]		= { .type = NLA_U32 },  };  static int parse_reg_rule(struct nlattr *tb[], @@ -4523,12 +4701,15 @@ static int parse_reg_rule(struct nlattr *tb[],  		power_rule->max_antenna_gain =  			nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); +	if (tb[NL80211_ATTR_DFS_CAC_TIME]) +		reg_rule->dfs_cac_ms = +			nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]); +  	return 0;  }  static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)  { -	int r;  	char *data = NULL;  	enum nl80211_user_reg_hint_type user_reg_hint_type; @@ -4541,11 +4722,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)  	if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))  		return -EINPROGRESS; -	if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) -		return -EINVAL; - -	data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); -  	if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])  		user_reg_hint_type =  		  nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); @@ -4555,14 +4731,16 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)  	switch (user_reg_hint_type) {  	case NL80211_USER_REG_HINT_USER:  	case NL80211_USER_REG_HINT_CELL_BASE: -		break; +		if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) +			return -EINVAL; + +		data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); +		return regulatory_hint_user(data, user_reg_hint_type); +	case NL80211_USER_REG_HINT_INDOOR: +		return regulatory_hint_indoor_user();  	default:  		return -EINVAL;  	} - -	r = regulatory_hint_user(data, user_reg_hint_type); - -	return r;  }  static int nl80211_get_mesh_config(struct sk_buff *skb, @@ -4984,6 +5162,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  		const struct ieee80211_reg_rule *reg_rule;  		const struct ieee80211_freq_range *freq_range;  		const struct ieee80211_power_rule *power_rule; +		unsigned int max_bandwidth_khz;  		reg_rule = ®dom->reg_rules[i];  		freq_range = ®_rule->freq_range; @@ -4993,6 +5172,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  		if (!nl_reg_rule)  			goto nla_put_failure_rcu; +		max_bandwidth_khz = freq_range->max_bandwidth_khz; +		if (!max_bandwidth_khz) +			max_bandwidth_khz = reg_get_max_bandwidth(regdom, +								  reg_rule); +  		if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,  				reg_rule->flags) ||  		    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START, @@ -5000,11 +5184,13 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)  		    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,  				freq_range->end_freq_khz) ||  		    nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW, -				freq_range->max_bandwidth_khz) || +				max_bandwidth_khz) ||  		    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,  				power_rule->max_antenna_gain) ||  		    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, -				power_rule->max_eirp)) +				power_rule->max_eirp) || +		    nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME, +				reg_rule->dfs_cac_ms))  			goto nla_put_failure_rcu;  		nla_nest_end(msg, nl_reg_rule); @@ -5032,7 +5218,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)  	char *alpha2 = NULL;  	int rem_reg_rules = 0, r = 0;  	u32 num_rules = 0, rule_idx = 0, size_of_regd; -	u8 dfs_region = 0; +	enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;  	struct ieee80211_regdomain *rd = NULL;  	if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) @@ -5053,6 +5239,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)  			return -EINVAL;  	} +	if (!reg_is_valid_request(alpha2)) +		return -EINVAL; +  	size_of_regd = sizeof(struct ieee80211_regdomain) +  		       num_rules * sizeof(struct ieee80211_reg_rule); @@ -5073,9 +5262,11 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)  	nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_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); +		r = nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, +			      nla_data(nl_reg_rule), nla_len(nl_reg_rule), +			      reg_rule_policy); +		if (r) +			goto bad_reg;  		r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);  		if (r)  			goto bad_reg; @@ -5140,7 +5331,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)  	if (!rdev->ops->scan)  		return -EOPNOTSUPP; -	if (rdev->scan_req) { +	if (rdev->scan_req || rdev->scan_msg) {  		err = -EBUSY;  		goto unlock;  	} @@ -5153,12 +5344,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)  			goto unlock;  		}  	} else { -		enum ieee80211_band band; -		n_channels = 0; - -		for (band = 0; band < IEEE80211_NUM_BANDS; band++) -			if (wiphy->bands[band]) -				n_channels += wiphy->bands[band]->n_channels; +		n_channels = ieee80211_get_num_supported_channels(wiphy);  	}  	if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) @@ -5283,6 +5469,10 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)  				err = -EINVAL;  				goto out_free;  			} + +			if (!wiphy->bands[band]) +				continue; +  			err = ieee80211_get_ratemask(wiphy->bands[band],  						     nla_data(attr),  						     nla_len(attr), @@ -5295,10 +5485,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {  		request->flags = nla_get_u32(  			info->attrs[NL80211_ATTR_SCAN_FLAGS]); -		if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && -		     !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || -		    ((request->flags & NL80211_SCAN_FLAG_FLUSH) && -		     !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) { +		if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && +		    !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {  			err = -EOPNOTSUPP;  			goto out_free;  		} @@ -5341,6 +5529,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  	enum ieee80211_band band;  	size_t ie_len;  	struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; +	s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;  	if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||  	    !rdev->ops->sched_scan_start) @@ -5364,11 +5553,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  		if (!n_channels)  			return -EINVAL;  	} else { -		n_channels = 0; - -		for (band = 0; band < IEEE80211_NUM_BANDS; band++) -			if (wiphy->bands[band]) -				n_channels += wiphy->bands[band]->n_channels; +		n_channels = ieee80211_get_num_supported_channels(wiphy);  	}  	if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) @@ -5379,11 +5564,40 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  	if (n_ssids > wiphy->max_sched_scan_ssids)  		return -EINVAL; -	if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) +	/* +	 * First, count the number of 'real' matchsets. Due to an issue with +	 * the old implementation, matchsets containing only the RSSI attribute +	 * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default' +	 * RSSI for all matchsets, rather than their own matchset for reporting +	 * all APs with a strong RSSI. This is needed to be compatible with +	 * older userspace that treated a matchset with only the RSSI as the +	 * global RSSI for all other matchsets - if there are other matchsets. +	 */ +	if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {  		nla_for_each_nested(attr,  				    info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], -				    tmp) -			n_match_sets++; +				    tmp) { +			struct nlattr *rssi; + +			err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, +					nla_data(attr), nla_len(attr), +					nl80211_match_policy); +			if (err) +				return err; +			/* add other standalone attributes here */ +			if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) { +				n_match_sets++; +				continue; +			} +			rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; +			if (rssi) +				default_match_rssi = nla_get_s32(rssi); +		} +	} + +	/* However, if there's no other matchset, add the RSSI one */ +	if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF) +		n_match_sets = 1;  	if (n_match_sets > wiphy->max_match_sets)  		return -EINVAL; @@ -5504,11 +5718,22 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  				    tmp) {  			struct nlattr *ssid, *rssi; -			nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, -				  nla_data(attr), nla_len(attr), -				  nl80211_match_policy); +			err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, +					nla_data(attr), nla_len(attr), +					nl80211_match_policy); +			if (err) +				goto out_free;  			ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];  			if (ssid) { +				if (WARN_ON(i >= n_match_sets)) { +					/* this indicates a programming error, +					 * the loop above should have verified +					 * things properly +					 */ +					err = -EINVAL; +					goto out_free; +				} +  				if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {  					err = -EINVAL;  					goto out_free; @@ -5517,19 +5742,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  				       nla_data(ssid), nla_len(ssid));  				request->match_sets[i].ssid.ssid_len =  					nla_len(ssid); +				/* special attribute - old implemenation w/a */ +				request->match_sets[i].rssi_thold = +					default_match_rssi; +				rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; +				if (rssi) +					request->match_sets[i].rssi_thold = +						nla_get_s32(rssi);  			} -			rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; -			if (rssi) -				request->rssi_thold = nla_get_u32(rssi); -			else -				request->rssi_thold = -						   NL80211_SCAN_RSSI_THOLD_OFF;  			i++;  		} + +		/* there was no other matchset, so the RSSI one is alone */ +		if (i == 0) +			request->match_sets[0].rssi_thold = default_match_rssi; + +		request->min_rssi_thold = INT_MAX; +		for (i = 0; i < n_match_sets; i++) +			request->min_rssi_thold = +				min(request->match_sets[i].rssi_thold, +				    request->min_rssi_thold); +	} else { +		request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;  	} -	if (info->attrs[NL80211_ATTR_IE]) { -		request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); +	if (ie_len) { +		request->ie_len = ie_len;  		memcpy((void *)request->ie,  		       nla_data(info->attrs[NL80211_ATTR_IE]),  		       request->ie_len); @@ -5538,10 +5776,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,  	if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {  		request->flags = nla_get_u32(  			info->attrs[NL80211_ATTR_SCAN_FLAGS]); -		if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && -		     !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || -		    ((request->flags & NL80211_SCAN_FLAG_FLUSH) && -		     !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) { +		if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && +		    !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {  			err = -EOPNOTSUPP;  			goto out_free;  		} @@ -5585,39 +5821,49 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,  	struct net_device *dev = info->user_ptr[1];  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct cfg80211_chan_def chandef; +	enum nl80211_dfs_regions dfs_region; +	unsigned int cac_time_ms;  	int err; +	dfs_region = reg_get_dfs_region(wdev->wiphy); +	if (dfs_region == NL80211_DFS_UNSET) +		return -EINVAL; +  	err = nl80211_parse_chandef(rdev, info, &chandef);  	if (err)  		return err; +	if (netif_carrier_ok(dev)) +		return -EBUSY; +  	if (wdev->cac_started)  		return -EBUSY; -	err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef); +	err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, +					    wdev->iftype);  	if (err < 0)  		return err;  	if (err == 0)  		return -EINVAL; -	if (chandef.chan->dfs_state != NL80211_DFS_USABLE) +	if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))  		return -EINVAL;  	if (!rdev->ops->start_radar_detection)  		return -EOPNOTSUPP; -	err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, -					   chandef.chan, CHAN_MODE_SHARED, -					   BIT(chandef.width)); -	if (err) -		return err; +	cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef); +	if (WARN_ON(!cac_time_ms)) +		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; -	err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef); +	err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef, +					       cac_time_ms);  	if (!err) { -		wdev->channel = chandef.chan; +		wdev->chandef = chandef;  		wdev->cac_started = true;  		wdev->cac_start_time = jiffies; +		wdev->cac_time_ms = cac_time_ms;  	}  	return err;  } @@ -5634,15 +5880,33 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)  	static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];  	u8 radar_detect_width = 0;  	int err; +	bool need_new_beacon = false; +	int len, i;  	if (!rdev->ops->channel_switch ||  	    !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))  		return -EOPNOTSUPP; -	/* may add IBSS support later */ -	if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && -	    dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) +	switch (dev->ieee80211_ptr->iftype) { +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_P2P_GO: +		need_new_beacon = true; + +		/* useless if AP is not running */ +		if (!wdev->beacon_interval) +			return -ENOTCONN; +		break; +	case NL80211_IFTYPE_ADHOC: +		if (!wdev->ssid_len) +			return -ENOTCONN; +		break; +	case NL80211_IFTYPE_MESH_POINT: +		if (!wdev->mesh_id_len) +			return -ENOTCONN; +		break; +	default:  		return -EOPNOTSUPP; +	}  	memset(¶ms, 0, sizeof(params)); @@ -5651,15 +5915,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)  		return -EINVAL;  	/* only important for AP, IBSS and mesh create IEs internally */ -	if (!info->attrs[NL80211_ATTR_CSA_IES]) -		return -EINVAL; - -	/* useless if AP is not running */ -	if (!wdev->beacon_interval) +	if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])  		return -EINVAL;  	params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); +	if (!need_new_beacon) +		goto skip_beacons; +  	err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);  	if (err)  		return err; @@ -5677,43 +5940,81 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)  	if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])  		return -EINVAL; -	params.counter_offset_beacon = -		nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); -	if (params.counter_offset_beacon >= params.beacon_csa.tail_len) +	len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); +	if (!len || (len % sizeof(u16)))  		return -EINVAL; -	/* sanity check - counters should be the same */ -	if (params.beacon_csa.tail[params.counter_offset_beacon] != -	    params.count) +	params.n_counter_offsets_beacon = len / sizeof(u16); +	if (rdev->wiphy.max_num_csa_counters && +	    (params.n_counter_offsets_beacon > +	     rdev->wiphy.max_num_csa_counters))  		return -EINVAL; +	params.counter_offsets_beacon = +		nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); + +	/* sanity checks - counters should fit and be the same */ +	for (i = 0; i < params.n_counter_offsets_beacon; i++) { +		u16 offset = params.counter_offsets_beacon[i]; + +		if (offset >= params.beacon_csa.tail_len) +			return -EINVAL; + +		if (params.beacon_csa.tail[offset] != params.count) +			return -EINVAL; +	} +  	if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) { -		params.counter_offset_presp = -			nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); -		if (params.counter_offset_presp >= -		    params.beacon_csa.probe_resp_len) +		len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); +		if (!len || (len % sizeof(u16)))  			return -EINVAL; -		if (params.beacon_csa.probe_resp[params.counter_offset_presp] != -		    params.count) +		params.n_counter_offsets_presp = len / sizeof(u16); +		if (rdev->wiphy.max_num_csa_counters && +		    (params.n_counter_offsets_beacon > +		     rdev->wiphy.max_num_csa_counters))  			return -EINVAL; + +		params.counter_offsets_presp = +			nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); + +		/* sanity checks - counters should fit and be the same */ +		for (i = 0; i < params.n_counter_offsets_presp; i++) { +			u16 offset = params.counter_offsets_presp[i]; + +			if (offset >= params.beacon_csa.probe_resp_len) +				return -EINVAL; + +			if (params.beacon_csa.probe_resp[offset] != +			    params.count) +				return -EINVAL; +		}  	} +skip_beacons:  	err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);  	if (err)  		return err; -	if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) +	if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef, +				     wdev->iftype))  		return -EINVAL; -	err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); -	if (err < 0) { +	err = cfg80211_chandef_dfs_required(wdev->wiphy, +					    ¶ms.chandef, +					    wdev->iftype); +	if (err < 0)  		return err; -	} else if (err) { + +	if (err > 0) {  		radar_detect_width = BIT(params.chandef.width);  		params.radar_required = true;  	} +	/* TODO: I left this here for now.  With channel switch, the +	 * verification is a bit more complicated, because we only do +	 * it later when the channel switch really happens. +	 */  	err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,  					   params.chandef.chan,  					   CHAN_MODE_SHARED, @@ -5724,7 +6025,11 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])  		params.block_tx = true; -	return rdev_channel_switch(rdev, dev, ¶ms); +	wdev_lock(wdev); +	err = rdev_channel_switch(rdev, dev, ¶ms); +	wdev_unlock(wdev); + +	return err;  }  static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, @@ -5936,12 +6241,12 @@ static int nl80211_dump_survey(struct sk_buff *skb,  			struct netlink_callback *cb)  {  	struct survey_info survey; -	struct cfg80211_registered_device *dev; +	struct cfg80211_registered_device *rdev;  	struct wireless_dev *wdev;  	int survey_idx = cb->args[2];  	int res; -	res = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev); +	res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);  	if (res)  		return res; @@ -5950,7 +6255,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,  		goto out_err;  	} -	if (!dev->ops->dump_survey) { +	if (!rdev->ops->dump_survey) {  		res = -EOPNOTSUPP;  		goto out_err;  	} @@ -5958,7 +6263,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,  	while (1) {  		struct ieee80211_channel *chan; -		res = rdev_dump_survey(dev, wdev->netdev, survey_idx, &survey); +		res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);  		if (res == -ENOENT)  			break;  		if (res) @@ -5970,7 +6275,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,  			goto out;  		} -		chan = ieee80211_get_channel(&dev->wiphy, +		chan = ieee80211_get_channel(&rdev->wiphy,  					     survey.channel->center_freq);  		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {  			survey_idx++; @@ -5989,7 +6294,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,  	cb->args[2] = survey_idx;  	res = skb->len;   out_err: -	nl80211_finish_wdev_dump(dev); +	nl80211_finish_wdev_dump(rdev);  	return res;  } @@ -6067,9 +6372,9 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)  		return -EOPNOTSUPP;  	bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); -	chan = ieee80211_get_channel(&rdev->wiphy, -		nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); -	if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) +	chan = nl80211_get_valid_chan(&rdev->wiphy, +				      info->attrs[NL80211_ATTR_WIPHY_FREQ]); +	if (!chan)  		return -EINVAL;  	ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); @@ -6222,9 +6527,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)  	bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); -	chan = ieee80211_get_channel(&rdev->wiphy, -		nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); -	if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) +	chan = nl80211_get_valid_chan(&rdev->wiphy, +				      info->attrs[NL80211_ATTR_WIPHY_FREQ]); +	if (!chan)  		return -EINVAL;  	ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); @@ -6465,7 +6770,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)  	if (err)  		return err; -	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef)) +	if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef, +				     NL80211_IFTYPE_ADHOC))  		return -EINVAL;  	switch (ibss.chandef.width) { @@ -6535,6 +6841,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)  	ibss.control_port =  		nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]); +	ibss.userspace_handles_dfs = +		nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]); +  	err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);  	if (err)  		kfree(connkeys); @@ -6584,12 +6893,103 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)  	return err;  } +static struct sk_buff * +__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, +			    int approxlen, u32 portid, u32 seq, +			    enum nl80211_commands cmd, +			    enum nl80211_attrs attr, +			    const struct nl80211_vendor_cmd_info *info, +			    gfp_t gfp) +{ +	struct sk_buff *skb; +	void *hdr; +	struct nlattr *data; -#ifdef CONFIG_NL80211_TESTMODE -static struct genl_multicast_group nl80211_testmode_mcgrp = { -	.name = "testmode", -}; +	skb = nlmsg_new(approxlen + 100, gfp); +	if (!skb) +		return NULL; + +	hdr = nl80211hdr_put(skb, portid, seq, 0, cmd); +	if (!hdr) { +		kfree_skb(skb); +		return NULL; +	} + +	if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) +		goto nla_put_failure; + +	if (info) { +		if (nla_put_u32(skb, NL80211_ATTR_VENDOR_ID, +				info->vendor_id)) +			goto nla_put_failure; +		if (nla_put_u32(skb, NL80211_ATTR_VENDOR_SUBCMD, +				info->subcmd)) +			goto nla_put_failure; +	} + +	data = nla_nest_start(skb, attr); + +	((void **)skb->cb)[0] = rdev; +	((void **)skb->cb)[1] = hdr; +	((void **)skb->cb)[2] = data; + +	return skb; + + nla_put_failure: +	kfree_skb(skb); +	return NULL; +} + +struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, +					   enum nl80211_commands cmd, +					   enum nl80211_attrs attr, +					   int vendor_event_idx, +					   int approxlen, gfp_t gfp) +{ +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); +	const struct nl80211_vendor_cmd_info *info; + +	switch (cmd) { +	case NL80211_CMD_TESTMODE: +		if (WARN_ON(vendor_event_idx != -1)) +			return NULL; +		info = NULL; +		break; +	case NL80211_CMD_VENDOR: +		if (WARN_ON(vendor_event_idx < 0 || +			    vendor_event_idx >= wiphy->n_vendor_events)) +			return NULL; +		info = &wiphy->vendor_events[vendor_event_idx]; +		break; +	default: +		WARN_ON(1); +		return NULL; +	} + +	return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, +					   cmd, attr, info, gfp); +} +EXPORT_SYMBOL(__cfg80211_alloc_event_skb); + +void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) +{ +	struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; +	void *hdr = ((void **)skb->cb)[1]; +	struct nlattr *data = ((void **)skb->cb)[2]; +	enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; + +	nla_nest_end(skb, data); +	genlmsg_end(skb, hdr); +	if (data->nla_type == NL80211_ATTR_VENDOR_DATA) +		mcgrp = NL80211_MCGRP_VENDOR; + +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, +				mcgrp, gfp); +} +EXPORT_SYMBOL(__cfg80211_send_event_skb); + +#ifdef CONFIG_NL80211_TESTMODE  static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)  {  	struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6612,11 +7012,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[NL80211_ATTR_TESTDATA])  		return -EINVAL; -	rdev->testmode_info = info; +	rdev->cur_cmd_info = info;  	err = rdev_testmode_cmd(rdev, wdev,  				nla_data(info->attrs[NL80211_ATTR_TESTDATA]),  				nla_len(info->attrs[NL80211_ATTR_TESTDATA])); -	rdev->testmode_info = NULL; +	rdev->cur_cmd_info = NULL;  	return err;  } @@ -6715,93 +7115,6 @@ static int nl80211_testmode_dump(struct sk_buff *skb,  	rtnl_unlock();  	return err;  } - -static struct sk_buff * -__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, -			      int approxlen, u32 portid, u32 seq, gfp_t gfp) -{ -	struct sk_buff *skb; -	void *hdr; -	struct nlattr *data; - -	skb = nlmsg_new(approxlen + 100, gfp); -	if (!skb) -		return NULL; - -	hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE); -	if (!hdr) { -		kfree_skb(skb); -		return NULL; -	} - -	if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) -		goto nla_put_failure; -	data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); - -	((void **)skb->cb)[0] = rdev; -	((void **)skb->cb)[1] = hdr; -	((void **)skb->cb)[2] = data; - -	return skb; - - nla_put_failure: -	kfree_skb(skb); -	return NULL; -} - -struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, -						  int approxlen) -{ -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - -	if (WARN_ON(!rdev->testmode_info)) -		return NULL; - -	return __cfg80211_testmode_alloc_skb(rdev, approxlen, -				rdev->testmode_info->snd_portid, -				rdev->testmode_info->snd_seq, -				GFP_KERNEL); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); - -int cfg80211_testmode_reply(struct sk_buff *skb) -{ -	struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; -	void *hdr = ((void **)skb->cb)[1]; -	struct nlattr *data = ((void **)skb->cb)[2]; - -	if (WARN_ON(!rdev->testmode_info)) { -		kfree_skb(skb); -		return -EINVAL; -	} - -	nla_nest_end(skb, data); -	genlmsg_end(skb, hdr); -	return genlmsg_reply(skb, rdev->testmode_info); -} -EXPORT_SYMBOL(cfg80211_testmode_reply); - -struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, -						  int approxlen, gfp_t gfp) -{ -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - -	return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); - -void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) -{ -	struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; -	void *hdr = ((void **)skb->cb)[1]; -	struct nlattr *data = ((void **)skb->cb)[2]; - -	nla_nest_end(skb, data); -	genlmsg_end(skb, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), skb, 0, -				nl80211_testmode_mcgrp.id, gfp); -} -EXPORT_SYMBOL(cfg80211_testmode_event);  #endif  static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) @@ -6853,6 +7166,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_MAC])  		connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); +	else if (info->attrs[NL80211_ATTR_MAC_HINT]) +		connect.bssid_hint = +			nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);  	connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);  	connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); @@ -6871,11 +7187,14 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)  	}  	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { -		connect.channel = -			ieee80211_get_channel(wiphy, -			    nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); -		if (!connect.channel || -		    connect.channel->flags & IEEE80211_CHAN_DISABLED) +		connect.channel = nl80211_get_valid_chan( +			wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]); +		if (!connect.channel) +			return -EINVAL; +	} else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) { +		connect.channel_hint = nl80211_get_valid_chan( +			wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]); +		if (!connect.channel_hint)  			return -EINVAL;  	} @@ -7042,6 +7361,7 @@ static int nl80211_tdls_mgmt(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];  	u8 action_code, dialog_token; +	u32 peer_capability = 0;  	u16 status_code;  	u8 *peer; @@ -7060,9 +7380,12 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)  	action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);  	status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);  	dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); +	if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]) +		peer_capability = +			nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);  	return rdev_tdls_mgmt(rdev, dev, peer, action_code, -			      dialog_token, status_code, +			      dialog_token, status_code, peer_capability,  			      nla_data(info->attrs[NL80211_ATTR_IE]),  			      nla_len(info->attrs[NL80211_ATTR_IE]));  } @@ -7223,11 +7546,73 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,  	return true;  } +static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map) +{ +	u16 mcs_mask = 0; + +	switch (vht_mcs_map) { +	case IEEE80211_VHT_MCS_NOT_SUPPORTED: +		break; +	case IEEE80211_VHT_MCS_SUPPORT_0_7: +		mcs_mask = 0x00FF; +		break; +	case IEEE80211_VHT_MCS_SUPPORT_0_8: +		mcs_mask = 0x01FF; +		break; +	case IEEE80211_VHT_MCS_SUPPORT_0_9: +		mcs_mask = 0x03FF; +		break; +	default: +		break; +	} + +	return mcs_mask; +} + +static void vht_build_mcs_mask(u16 vht_mcs_map, +			       u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ +	u8 nss; + +	for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { +		vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03); +		vht_mcs_map >>= 2; +	} +} + +static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, +			     struct nl80211_txrate_vht *txrate, +			     u16 mcs[NL80211_VHT_NSS_MAX]) +{ +	u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); +	u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {}; +	u8 i; + +	if (!sband->vht_cap.vht_supported) +		return false; + +	memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX); + +	/* Build vht_mcs_mask from VHT capabilities */ +	vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask); + +	for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { +		if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) +			mcs[i] = txrate->mcs[i]; +		else +			return false; +	} + +	return true; +} +  static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {  	[NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,  				    .len = NL80211_MAX_SUPP_RATES }, -	[NL80211_TXRATE_MCS] = { .type = NLA_BINARY, -				 .len = NL80211_MAX_SUPP_HT_RATES }, +	[NL80211_TXRATE_HT] = { .type = NLA_BINARY, +				.len = NL80211_MAX_SUPP_HT_RATES }, +	[NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)}, +	[NL80211_TXRATE_GI] = { .type = NLA_U8 },  };  static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, @@ -7240,9 +7625,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,  	struct net_device *dev = info->user_ptr[1];  	struct nlattr *tx_rates;  	struct ieee80211_supported_band *sband; - -	if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) -		return -EINVAL; +	u16 vht_tx_mcs_map;  	if (!rdev->ops->set_bitrate_mask)  		return -EOPNOTSUPP; @@ -7251,32 +7634,44 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,  	/* Default to all rates enabled */  	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {  		sband = rdev->wiphy.bands[i]; -		mask.control[i].legacy = -			sband ? (1 << sband->n_bitrates) - 1 : 0; -		if (sband) -			memcpy(mask.control[i].mcs, -			       sband->ht_cap.mcs.rx_mask, -			       sizeof(mask.control[i].mcs)); -		else -			memset(mask.control[i].mcs, 0, -			       sizeof(mask.control[i].mcs)); + +		if (!sband) +			continue; + +		mask.control[i].legacy = (1 << sband->n_bitrates) - 1; +		memcpy(mask.control[i].ht_mcs, +		       sband->ht_cap.mcs.rx_mask, +		       sizeof(mask.control[i].ht_mcs)); + +		if (!sband->vht_cap.vht_supported) +			continue; + +		vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); +		vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);  	} +	/* if no rates are given set it back to the defaults */ +	if (!info->attrs[NL80211_ATTR_TX_RATES]) +		goto out; +  	/*  	 * The nested attribute uses enum nl80211_band as the index. This maps  	 * directly to the enum ieee80211_band values used in cfg80211.  	 */  	BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); -	nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) -	{ +	nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {  		enum ieee80211_band band = nla_type(tx_rates); +		int err; +  		if (band < 0 || band >= IEEE80211_NUM_BANDS)  			return -EINVAL;  		sband = rdev->wiphy.bands[band];  		if (sband == NULL)  			return -EINVAL; -		nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), -			  nla_len(tx_rates), nl80211_txattr_policy); +		err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), +				nla_len(tx_rates), nl80211_txattr_policy); +		if (err) +			return err;  		if (tb[NL80211_TXRATE_LEGACY]) {  			mask.control[band].legacy = rateset_to_mask(  				sband, @@ -7286,31 +7681,50 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,  			    nla_len(tb[NL80211_TXRATE_LEGACY]))  				return -EINVAL;  		} -		if (tb[NL80211_TXRATE_MCS]) { +		if (tb[NL80211_TXRATE_HT]) {  			if (!ht_rateset_to_mask(  					sband, -					nla_data(tb[NL80211_TXRATE_MCS]), -					nla_len(tb[NL80211_TXRATE_MCS]), -					mask.control[band].mcs)) +					nla_data(tb[NL80211_TXRATE_HT]), +					nla_len(tb[NL80211_TXRATE_HT]), +					mask.control[band].ht_mcs)) +				return -EINVAL; +		} +		if (tb[NL80211_TXRATE_VHT]) { +			if (!vht_set_mcs_mask( +					sband, +					nla_data(tb[NL80211_TXRATE_VHT]), +					mask.control[band].vht_mcs)) +				return -EINVAL; +		} +		if (tb[NL80211_TXRATE_GI]) { +			mask.control[band].gi = +				nla_get_u8(tb[NL80211_TXRATE_GI]); +			if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)  				return -EINVAL;  		}  		if (mask.control[band].legacy == 0) { -			/* don't allow empty legacy rates if HT -			 * is not even supported. */ -			if (!rdev->wiphy.bands[band]->ht_cap.ht_supported) +			/* don't allow empty legacy rates if HT or VHT +			 * are not even supported. +			 */ +			if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || +			      rdev->wiphy.bands[band]->vht_cap.vht_supported))  				return -EINVAL;  			for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) -				if (mask.control[band].mcs[i]) -					break; +				if (mask.control[band].ht_mcs[i]) +					goto out; + +			for (i = 0; i < NL80211_VHT_NSS_MAX; i++) +				if (mask.control[band].vht_mcs[i]) +					goto out;  			/* legacy and mcs rates may not be both empty */ -			if (i == IEEE80211_HT_MCS_MASK_LEN) -				return -EINVAL; +			return -EINVAL;  		}  	} +out:  	return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);  } @@ -7358,10 +7772,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  	void *hdr = NULL;  	u64 cookie;  	struct sk_buff *msg = NULL; -	unsigned int wait = 0; -	bool offchan, no_cck, dont_wait_for_ack; - -	dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; +	struct cfg80211_mgmt_tx_params params = { +		.dont_wait_for_ack = +			info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK], +	};  	if (!info->attrs[NL80211_ATTR_FRAME])  		return -EINVAL; @@ -7388,24 +7802,24 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  	if (info->attrs[NL80211_ATTR_DURATION]) {  		if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))  			return -EINVAL; -		wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); +		params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);  		/*  		 * We should wait on the channel for at least a minimum amount  		 * of time (10ms) but no longer than the driver supports.  		 */ -		if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || -		    wait > rdev->wiphy.max_remain_on_channel_duration) +		if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || +		    params.wait > rdev->wiphy.max_remain_on_channel_duration)  			return -EINVAL;  	} -	offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; +	params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; -	if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) +	if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))  		return -EINVAL; -	no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); +	params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);  	/* get the channel if any has been specified, otherwise pass NULL to  	 * the driver. The latter will use the current one @@ -7417,10 +7831,31 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  			return err;  	} -	if (!chandef.chan && offchan) +	if (!chandef.chan && params.offchan)  		return -EINVAL; -	if (!dont_wait_for_ack) { +	params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); +	params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); + +	if (info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]) { +		int len = nla_len(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]); +		int i; + +		if (len % sizeof(u16)) +			return -EINVAL; + +		params.n_csa_offsets = len / sizeof(u16); +		params.csa_offsets = +			nla_data(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]); + +		/* check that all the offsets fit the frame */ +		for (i = 0; i < params.n_csa_offsets; i++) { +			if (params.csa_offsets[i] >= params.len) +				return -EINVAL; +		} +	} + +	if (!params.dont_wait_for_ack) {  		msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  		if (!msg)  			return -ENOMEM; @@ -7433,10 +7868,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)  		}  	} -	err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait, -				    nla_data(info->attrs[NL80211_ATTR_FRAME]), -				    nla_len(info->attrs[NL80211_ATTR_FRAME]), -				    no_cck, dont_wait_for_ack, &cookie); +	params.chan = chandef.chan; +	err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie);  	if (err)  		goto free_msg; @@ -7564,8 +7997,8 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)  	return err;  } -static struct nla_policy -nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { +static const struct nla_policy +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {  	[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },  	[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },  	[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, @@ -8131,6 +8564,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)  		nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],  				    rem) { +			u8 *mask_pat; +  			nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),  				  nla_len(pat), NULL);  			err = -EINVAL; @@ -8154,19 +8589,18 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)  				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) { +			mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); +			if (!mask_pat) {  				err = -ENOMEM;  				goto error;  			} -			new_triggers.patterns[i].pattern = -				new_triggers.patterns[i].mask + mask_len; -			memcpy(new_triggers.patterns[i].mask, -			       nla_data(pat_tb[NL80211_PKTPAT_MASK]), +			new_triggers.patterns[i].mask = mask_pat; +			memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),  			       mask_len); +			mask_pat += mask_len; +			new_triggers.patterns[i].pattern = mask_pat;  			new_triggers.patterns[i].pattern_len = pat_len; -			memcpy(new_triggers.patterns[i].pattern, +			memcpy(mask_pat,  			       nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),  			       pat_len);  			i++; @@ -8358,6 +8792,8 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,  	nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],  			    rem) { +		u8 *mask_pat; +  		nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),  			  nla_len(pat), NULL);  		if (!pat_tb[NL80211_PKTPAT_MASK] || @@ -8379,17 +8815,19 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,  			return -EINVAL;  		new_rule->patterns[i].pkt_offset = pkt_offset; -		new_rule->patterns[i].mask = -			kmalloc(mask_len + pat_len, GFP_KERNEL); -		if (!new_rule->patterns[i].mask) +		mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); +		if (!mask_pat)  			return -ENOMEM; -		new_rule->patterns[i].pattern = -			new_rule->patterns[i].mask + mask_len; -		memcpy(new_rule->patterns[i].mask, -		       nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); + +		new_rule->patterns[i].mask = mask_pat; +		memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]), +		       mask_len); + +		mask_pat += mask_len; +		new_rule->patterns[i].pattern = mask_pat;  		new_rule->patterns[i].pattern_len = pat_len; -		memcpy(new_rule->patterns[i].pattern, -		       nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); +		memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), +		       pat_len);  		i++;  	} @@ -8634,9 +9072,8 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)  	if (wdev->p2p_started)  		return 0; -	err = cfg80211_can_add_interface(rdev, wdev->iftype); -	if (err) -		return err; +	if (rfkill_blocked(rdev->rfkill)) +		return -ERFKILL;  	err = rdev_start_p2p_device(rdev, wdev);  	if (err) @@ -8770,6 +9207,162 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,  	return 0;  } +static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) +{ +	struct cfg80211_registered_device *rdev = info->user_ptr[0]; +	struct wireless_dev *wdev = +		__cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); +	int i, err; +	u32 vid, subcmd; + +	if (!rdev->wiphy.vendor_commands) +		return -EOPNOTSUPP; + +	if (IS_ERR(wdev)) { +		err = PTR_ERR(wdev); +		if (err != -EINVAL) +			return err; +		wdev = NULL; +	} else if (wdev->wiphy != &rdev->wiphy) { +		return -EINVAL; +	} + +	if (!info->attrs[NL80211_ATTR_VENDOR_ID] || +	    !info->attrs[NL80211_ATTR_VENDOR_SUBCMD]) +		return -EINVAL; + +	vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]); +	subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]); +	for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { +		const struct wiphy_vendor_command *vcmd; +		void *data = NULL; +		int len = 0; + +		vcmd = &rdev->wiphy.vendor_commands[i]; + +		if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) +			continue; + +		if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | +				   WIPHY_VENDOR_CMD_NEED_NETDEV)) { +			if (!wdev) +				return -EINVAL; +			if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && +			    !wdev->netdev) +				return -EINVAL; + +			if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { +				if (wdev->netdev && +				    !netif_running(wdev->netdev)) +					return -ENETDOWN; +				if (!wdev->netdev && !wdev->p2p_started) +					return -ENETDOWN; +			} +		} else { +			wdev = NULL; +		} + +		if (info->attrs[NL80211_ATTR_VENDOR_DATA]) { +			data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]); +			len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]); +		} + +		rdev->cur_cmd_info = info; +		err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev, +							  data, len); +		rdev->cur_cmd_info = NULL; +		return err; +	} + +	return -EOPNOTSUPP; +} + +struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, +					   enum nl80211_commands cmd, +					   enum nl80211_attrs attr, +					   int approxlen) +{ +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + +	if (WARN_ON(!rdev->cur_cmd_info)) +		return NULL; + +	return __cfg80211_alloc_vendor_skb(rdev, approxlen, +					   rdev->cur_cmd_info->snd_portid, +					   rdev->cur_cmd_info->snd_seq, +					   cmd, attr, NULL, GFP_KERNEL); +} +EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); + +int cfg80211_vendor_cmd_reply(struct sk_buff *skb) +{ +	struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; +	void *hdr = ((void **)skb->cb)[1]; +	struct nlattr *data = ((void **)skb->cb)[2]; + +	if (WARN_ON(!rdev->cur_cmd_info)) { +		kfree_skb(skb); +		return -EINVAL; +	} + +	nla_nest_end(skb, data); +	genlmsg_end(skb, hdr); +	return genlmsg_reply(skb, rdev->cur_cmd_info); +} +EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); + + +static int nl80211_set_qos_map(struct sk_buff *skb, +			       struct genl_info *info) +{ +	struct cfg80211_registered_device *rdev = info->user_ptr[0]; +	struct cfg80211_qos_map *qos_map = NULL; +	struct net_device *dev = info->user_ptr[1]; +	u8 *pos, len, num_des, des_len, des; +	int ret; + +	if (!rdev->ops->set_qos_map) +		return -EOPNOTSUPP; + +	if (info->attrs[NL80211_ATTR_QOS_MAP]) { +		pos = nla_data(info->attrs[NL80211_ATTR_QOS_MAP]); +		len = nla_len(info->attrs[NL80211_ATTR_QOS_MAP]); + +		if (len % 2 || len < IEEE80211_QOS_MAP_LEN_MIN || +		    len > IEEE80211_QOS_MAP_LEN_MAX) +			return -EINVAL; + +		qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL); +		if (!qos_map) +			return -ENOMEM; + +		num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1; +		if (num_des) { +			des_len = num_des * +				sizeof(struct cfg80211_dscp_exception); +			memcpy(qos_map->dscp_exception, pos, des_len); +			qos_map->num_des = num_des; +			for (des = 0; des < num_des; des++) { +				if (qos_map->dscp_exception[des].up > 7) { +					kfree(qos_map); +					return -EINVAL; +				} +			} +			pos += des_len; +		} +		memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN); +	} + +	wdev_lock(dev->ieee80211_ptr); +	ret = nl80211_key_allowed(dev->ieee80211_ptr); +	if (!ret) +		ret = rdev_set_qos_map(rdev, dev, qos_map); +	wdev_unlock(dev->ieee80211_ptr); + +	kfree(qos_map); +	return ret; +} +  #define NL80211_FLAG_NEED_WIPHY		0x01  #define NL80211_FLAG_NEED_NETDEV	0x02  #define NL80211_FLAG_NEED_RTNL		0x04 @@ -8781,7 +9374,7 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,  #define NL80211_FLAG_NEED_WDEV_UP	(NL80211_FLAG_NEED_WDEV |\  					 NL80211_FLAG_CHECK_NETDEV_UP) -static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, +static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,  			    struct genl_info *info)  {  	struct cfg80211_registered_device *rdev; @@ -8813,7 +9406,7 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,  		}  		dev = wdev->netdev; -		rdev = wiphy_to_dev(wdev->wiphy); +		rdev = wiphy_to_rdev(wdev->wiphy);  		if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {  			if (!dev) { @@ -8850,7 +9443,7 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,  	return 0;  } -static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, +static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,  			      struct genl_info *info)  {  	if (info->user_ptr[1]) { @@ -8867,7 +9460,7 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,  		rtnl_unlock();  } -static struct genl_ops nl80211_ops[] = { +static const struct genl_ops nl80211_ops[] = {  	{  		.cmd = NL80211_CMD_GET_WIPHY,  		.doit = nl80211_get_wiphy, @@ -9494,41 +10087,46 @@ static struct genl_ops nl80211_ops[] = {  		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |  				  NL80211_FLAG_NEED_RTNL,  	}, -}; - -static struct genl_multicast_group nl80211_mlme_mcgrp = { -	.name = "mlme", -}; - -/* multicast groups */ -static struct genl_multicast_group nl80211_config_mcgrp = { -	.name = "config", -}; -static struct genl_multicast_group nl80211_scan_mcgrp = { -	.name = "scan", -}; -static struct genl_multicast_group nl80211_regulatory_mcgrp = { -	.name = "regulatory", +	{ +		.cmd = NL80211_CMD_VENDOR, +		.doit = nl80211_vendor_cmd, +		.policy = nl80211_policy, +		.flags = GENL_ADMIN_PERM, +		.internal_flags = NL80211_FLAG_NEED_WIPHY | +				  NL80211_FLAG_NEED_RTNL, +	}, +	{ +		.cmd = NL80211_CMD_SET_QOS_MAP, +		.doit = nl80211_set_qos_map, +		.policy = nl80211_policy, +		.flags = GENL_ADMIN_PERM, +		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP | +				  NL80211_FLAG_NEED_RTNL, +	},  };  /* notification functions */ -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) +void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, +			  enum nl80211_commands cmd)  {  	struct sk_buff *msg;  	struct nl80211_dump_wiphy_state state = {}; +	WARN_ON(cmd != NL80211_CMD_NEW_WIPHY && +		cmd != NL80211_CMD_DEL_WIPHY); +  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  	if (!msg)  		return; -	if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, &state) < 0) { +	if (nl80211_send_wiphy(rdev, cmd, msg, 0, 0, 0, &state) < 0) {  		nlmsg_free(msg);  		return;  	} -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_config_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_CONFIG, GFP_KERNEL);  }  static int nl80211_add_scan_req(struct sk_buff *msg, @@ -9563,8 +10161,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); +	if (req->flags && +	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags)) +		goto nla_put_failure;  	return 0;   nla_put_failure: @@ -9637,46 +10236,37 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,  		return;  	} -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_scan_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_SCAN, GFP_KERNEL);  } -void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, -			    struct wireless_dev *wdev) +struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, +				       struct wireless_dev *wdev, bool aborted)  {  	struct sk_buff *msg;  	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  	if (!msg) -		return; +		return NULL;  	if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, -				  NL80211_CMD_NEW_SCAN_RESULTS) < 0) { +				  aborted ? NL80211_CMD_SCAN_ABORTED : +					    NL80211_CMD_NEW_SCAN_RESULTS) < 0) {  		nlmsg_free(msg); -		return; +		return NULL;  	} -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_scan_mcgrp.id, GFP_KERNEL); +	return msg;  } -void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, -			       struct wireless_dev *wdev) +void nl80211_send_scan_result(struct cfg80211_registered_device *rdev, +			      struct sk_buff *msg)  { -	struct sk_buff *msg; - -	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);  	if (!msg)  		return; -	if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, -				  NL80211_CMD_SCAN_ABORTED) < 0) { -		nlmsg_free(msg); -		return; -	} - -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_scan_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_SCAN, GFP_KERNEL);  }  void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, @@ -9694,8 +10284,8 @@ void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,  		return;  	} -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_scan_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_SCAN, GFP_KERNEL);  }  void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, @@ -9712,8 +10302,8 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,  		return;  	} -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_scan_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_SCAN, GFP_KERNEL);  }  /* @@ -9767,8 +10357,8 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)  	genlmsg_end(msg, hdr);  	rcu_read_lock(); -	genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, -				GFP_ATOMIC); +	genlmsg_multicast_allns(&nl80211_fam, msg, 0, +				NL80211_MCGRP_REGULATORY, GFP_ATOMIC);  	rcu_read_unlock();  	return; @@ -9803,8 +10393,8 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -9849,7 +10439,7 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	const struct ieee80211_mgmt *mgmt = (void *)buf;  	u32 cmd; @@ -9891,8 +10481,8 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -9947,8 +10537,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -9986,8 +10576,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10024,8 +10614,8 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, GFP_KERNEL);  	return;   nla_put_failure: @@ -10058,8 +10648,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10071,7 +10661,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,  					const u8* ie, u8 ie_len, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct sk_buff *msg;  	void *hdr; @@ -10099,8 +10689,8 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10138,8 +10728,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10191,8 +10781,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,  	genlmsg_end(msg, hdr);  	rcu_read_lock(); -	genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, -				GFP_ATOMIC); +	genlmsg_multicast_allns(&nl80211_fam, msg, 0, +				NL80211_MCGRP_REGULATORY, GFP_ATOMIC);  	rcu_read_unlock();  	return; @@ -10237,8 +10827,8 @@ static void nl80211_send_remain_on_chan_event(  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10251,7 +10841,7 @@ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,  			       unsigned int duration, gfp_t gfp)  {  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);  	nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, @@ -10265,7 +10855,7 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,  					gfp_t gfp)  {  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);  	nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, @@ -10277,7 +10867,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,  		      struct station_info *sinfo, gfp_t gfp)  {  	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	trace_cfg80211_new_sta(dev, mac_addr, sinfo); @@ -10292,15 +10882,15 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,  		return;  	} -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  }  EXPORT_SYMBOL(cfg80211_new_sta);  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); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	void *hdr; @@ -10322,8 +10912,8 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10337,7 +10927,7 @@ void cfg80211_conn_failed(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); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	void *hdr; @@ -10358,8 +10948,8 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10372,7 +10962,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,  				       const u8 *addr, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct sk_buff *msg;  	void *hdr;  	u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid); @@ -10492,7 +11082,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,  			     const u8 *buf, size_t len, bool ack, gfp_t gfp)  {  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct net_device *netdev = wdev->netdev;  	struct sk_buff *msg;  	void *hdr; @@ -10520,8 +11110,8 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10536,7 +11126,7 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	struct nlattr *pinfoattr;  	void *hdr; @@ -10569,8 +11159,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10614,8 +11204,8 @@ static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10628,7 +11218,7 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	trace_cfg80211_gtk_rekey_notify(dev, bssid);  	nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); @@ -10672,8 +11262,8 @@ nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10686,7 +11276,7 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);  	nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); @@ -10719,8 +11309,8 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10733,21 +11323,21 @@ void cfg80211_ch_switch_notify(struct net_device *dev,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); -	trace_cfg80211_ch_switch_notify(dev, chandef); +	ASSERT_WDEV_LOCK(wdev); -	wdev_lock(wdev); +	trace_cfg80211_ch_switch_notify(dev, chandef);  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && -		    wdev->iftype != NL80211_IFTYPE_P2P_GO)) -		goto out; +		    wdev->iftype != NL80211_IFTYPE_P2P_GO && +		    wdev->iftype != NL80211_IFTYPE_ADHOC && +		    wdev->iftype != NL80211_IFTYPE_MESH_POINT)) +		return; -	wdev->channel = chandef->chan; +	wdev->chandef = *chandef; +	wdev->preset_chandef = *chandef;  	nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); -out: -	wdev_unlock(wdev); -	return;  }  EXPORT_SYMBOL(cfg80211_ch_switch_notify); @@ -10757,7 +11347,7 @@ void cfg80211_cqm_txe_notify(struct net_device *dev,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	struct nlattr *pinfoattr;  	void *hdr; @@ -10794,8 +11384,8 @@ void cfg80211_cqm_txe_notify(struct net_device *dev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10806,7 +11396,7 @@ EXPORT_SYMBOL(cfg80211_cqm_txe_notify);  void  nl80211_radar_notify(struct cfg80211_registered_device *rdev, -		     struct cfg80211_chan_def *chandef, +		     const struct cfg80211_chan_def *chandef,  		     enum nl80211_radar_event event,  		     struct net_device *netdev, gfp_t gfp)  { @@ -10843,8 +11433,8 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10857,7 +11447,7 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,  {  	struct wireless_dev *wdev = dev->ieee80211_ptr;  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	struct nlattr *pinfoattr;  	void *hdr; @@ -10890,8 +11480,8 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10904,7 +11494,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,  			   u64 cookie, bool acked, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct sk_buff *msg;  	void *hdr; @@ -10930,8 +11520,8 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -10944,7 +11534,7 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,  				 const u8 *frame, size_t len,  				 int freq, int sig_dbm)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	void *hdr;  	struct cfg80211_beacon_registration *reg; @@ -10991,7 +11581,7 @@ 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 cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct sk_buff *msg;  	void *hdr;  	int size = 200; @@ -11021,6 +11611,8 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,  		struct nlattr *reasons;  		reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); +		if (!reasons) +			goto free_msg;  		if (wakeup->disconnect &&  		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) @@ -11046,16 +11638,18 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,  				wakeup->pattern_idx))  			goto free_msg; -		if (wakeup->tcp_match) -			nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH); +		if (wakeup->tcp_match && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH)) +			goto free_msg; -		if (wakeup->tcp_connlost) -			nla_put_flag(msg, -				     NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST); +		if (wakeup->tcp_connlost && +		    nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST)) +			goto free_msg; -		if (wakeup->tcp_nomoretokens) -			nla_put_flag(msg, -				NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS); +		if (wakeup->tcp_nomoretokens && +		    nla_put_flag(msg, +				 NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS)) +			goto free_msg;  		if (wakeup->packet) {  			u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; @@ -11082,8 +11676,8 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   free_msg: @@ -11097,7 +11691,7 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,  				u16 reason_code, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct sk_buff *msg;  	void *hdr; @@ -11124,8 +11718,8 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, gfp); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, gfp);  	return;   nla_put_failure: @@ -11149,9 +11743,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb,  	rcu_read_lock();  	list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { -		list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) +		bool schedule_destroy_work = false; + +		list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {  			cfg80211_mlme_unregister_socket(wdev, notify->portid); +			if (wdev->owner_nlportid == notify->portid) +				schedule_destroy_work = true; +		} +  		spin_lock_bh(&rdev->beacon_registrations_lock);  		list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,  					 list) { @@ -11162,11 +11762,24 @@ static int nl80211_netlink_notify(struct notifier_block * nb,  			}  		}  		spin_unlock_bh(&rdev->beacon_registrations_lock); + +		if (schedule_destroy_work) { +			struct cfg80211_iface_destroy *destroy; + +			destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC); +			if (destroy) { +				destroy->nlportid = notify->portid; +				spin_lock(&rdev->destroy_list_lock); +				list_add(&destroy->list, &rdev->destroy_list); +				spin_unlock(&rdev->destroy_list_lock); +				schedule_work(&rdev->destroy_work); +			} +		}  	}  	rcu_read_unlock(); -	return NOTIFY_DONE; +	return NOTIFY_OK;  }  static struct notifier_block nl80211_netlink_notifier = { @@ -11177,7 +11790,7 @@ void cfg80211_ft_event(struct net_device *netdev,  		       struct cfg80211_ft_event_params *ft_event)  {  	struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct sk_buff *msg;  	void *hdr; @@ -11191,24 +11804,29 @@ void cfg80211_ft_event(struct net_device *netdev,  		return;  	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT); -	if (!hdr) { -		nlmsg_free(msg); -		return; -	} +	if (!hdr) +		goto out; + +	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || +	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || +	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap)) +		goto out; -	nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); -	nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); -	nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap); -	if (ft_event->ies) -		nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies); -	if (ft_event->ric_ies) -		nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len, -			ft_event->ric_ies); +	if (ft_event->ies && +	    nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies)) +		goto out; +	if (ft_event->ric_ies && +	    nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len, +		    ft_event->ric_ies)) +		goto out;  	genlmsg_end(msg, hdr); -	genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, -				nl80211_mlme_mcgrp.id, GFP_KERNEL); +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, +				NL80211_MCGRP_MLME, GFP_KERNEL); +	return; + out: +	nlmsg_free(msg);  }  EXPORT_SYMBOL(cfg80211_ft_event); @@ -11219,7 +11837,7 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)  	void *hdr;  	u32 nlportid; -	rdev = wiphy_to_dev(wdev->wiphy); +	rdev = wiphy_to_rdev(wdev->wiphy);  	if (!rdev->crit_proto_nlportid)  		return; @@ -11251,38 +11869,45 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)  }  EXPORT_SYMBOL(cfg80211_crit_proto_stopped); -/* initialisation/exit functions */ - -int nl80211_init(void) +void nl80211_send_ap_stopped(struct wireless_dev *wdev)  { -	int err; +	struct wiphy *wiphy = wdev->wiphy; +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); +	struct sk_buff *msg; +	void *hdr; -	err = genl_register_family_with_ops(&nl80211_fam, -		nl80211_ops, ARRAY_SIZE(nl80211_ops)); -	if (err) -		return err; +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); +	if (!msg) +		return; -	err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp); -	if (err) -		goto err_out; +	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_STOP_AP); +	if (!hdr) +		goto out; -	err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); -	if (err) -		goto err_out; +	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || +	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex) || +	    nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) +		goto out; -	err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp); -	if (err) -		goto err_out; +	genlmsg_end(msg, hdr); -	err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); -	if (err) -		goto err_out; +	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0, +				NL80211_MCGRP_MLME, GFP_KERNEL); +	return; + out: +	nlmsg_free(msg); +} -#ifdef CONFIG_NL80211_TESTMODE -	err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); +/* initialisation/exit functions */ + +int nl80211_init(void) +{ +	int err; + +	err = genl_register_family_with_ops_groups(&nl80211_fam, nl80211_ops, +						   nl80211_mcgrps);  	if (err) -		goto err_out; -#endif +		return err;  	err = netlink_register_notifier(&nl80211_netlink_notifier);  	if (err) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 2c0f2b3c07c..49c9a482dd1 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -5,13 +5,14 @@  int nl80211_init(void);  void nl80211_exit(void); -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, +			  enum nl80211_commands cmd);  void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,  			     struct wireless_dev *wdev); -void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, -			    struct wireless_dev *wdev); -void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, -			       struct wireless_dev *wdev); +struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, +				       struct wireless_dev *wdev, bool aborted); +void nl80211_send_scan_result(struct cfg80211_registered_device *rdev, +			      struct sk_buff *msg);  void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,  			     struct net_device *netdev, u32 cmd);  void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, @@ -70,10 +71,12 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,  void  nl80211_radar_notify(struct cfg80211_registered_device *rdev, -		     struct cfg80211_chan_def *chandef, +		     const struct cfg80211_chan_def *chandef,  		     enum nl80211_radar_event event,  		     struct net_device *netdev, gfp_t gfp); +void nl80211_send_ap_stopped(struct wireless_dev *wdev); +  void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);  #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c index 7d604c06c3d..722da616438 100644 --- a/net/wireless/radiotap.c +++ b/net/wireless/radiotap.c @@ -97,6 +97,10 @@ int ieee80211_radiotap_iterator_init(  	struct ieee80211_radiotap_header *radiotap_header,  	int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)  { +	/* check the radiotap header can actually be present */ +	if (max_length < sizeof(struct ieee80211_radiotap_header)) +		return -EINVAL; +  	/* Linux only supports version 0 radiotap format */  	if (radiotap_header->it_version)  		return -EINVAL; @@ -120,6 +124,10 @@ int ieee80211_radiotap_iterator_init(  	/* find payload start allowing for extended bitmap(s) */  	if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { +		if ((unsigned long)iterator->_arg - +		    (unsigned long)iterator->_rtheader + sizeof(uint32_t) > +		    (unsigned long)iterator->_max_length) +			return -EINVAL;  		while (get_unaligned_le32(iterator->_arg) &  					(1 << IEEE80211_RADIOTAP_EXT)) {  			iterator->_arg += sizeof(uint32_t); @@ -131,7 +139,8 @@ int ieee80211_radiotap_iterator_init(  			 */  			if ((unsigned long)iterator->_arg - -			    (unsigned long)iterator->_rtheader > +			    (unsigned long)iterator->_rtheader + +			    sizeof(uint32_t) >  			    (unsigned long)iterator->_max_length)  				return -EINVAL;  		} diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 37ce9fdfe93..d95bbe34813 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -199,7 +199,7 @@ static inline int rdev_change_station(struct cfg80211_registered_device *rdev,  }  static inline int rdev_get_station(struct cfg80211_registered_device *rdev, -				   struct net_device *dev, u8 *mac, +				   struct net_device *dev, const u8 *mac,  				   struct station_info *sinfo)  {  	int ret; @@ -624,16 +624,12 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,  static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,  			       struct wireless_dev *wdev, -			       struct ieee80211_channel *chan, bool offchan, -			       unsigned int wait, const u8 *buf, size_t len, -			       bool no_cck, bool dont_wait_for_ack, u64 *cookie) +			       struct cfg80211_mgmt_tx_params *params, +			       u64 *cookie)  {  	int ret; -	trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, -			   wait, no_cck, dont_wait_for_ack); -	ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, -				  wait, buf, len, no_cck, -				  dont_wait_for_ack, cookie); +	trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params); +	ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie);  	trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);  	return ret;  } @@ -773,13 +769,16 @@ static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev,  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) +				 u16 status_code, u32 peer_capability, +				 const u8 *buf, size_t len)  {  	int ret;  	trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code, -			     dialog_token, status_code, buf, len); +			     dialog_token, status_code, peer_capability, +			     buf, len);  	ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, -				   dialog_token, status_code, buf, len); +				   dialog_token, status_code, peer_capability, +				   buf, len);  	trace_rdev_return_int(&rdev->wiphy, ret);  	return ret;  } @@ -936,4 +935,32 @@ static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,  	return ret;  } +static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev, +				   struct net_device *dev, +				   struct cfg80211_qos_map *qos_map) +{ +	int ret = -EOPNOTSUPP; + +	if (rdev->ops->set_qos_map) { +		trace_rdev_set_qos_map(&rdev->wiphy, dev, qos_map); +		ret = rdev->ops->set_qos_map(&rdev->wiphy, dev, qos_map); +		trace_rdev_return_int(&rdev->wiphy, ret); +	} + +	return ret; +} + +static inline int +rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev, +		      struct net_device *dev, struct cfg80211_chan_def *chandef) +{ +	int ret; + +	trace_rdev_set_ap_chanwidth(&rdev->wiphy, dev, chandef); +	ret = rdev->ops->set_ap_chanwidth(&rdev->wiphy, dev, chandef); +	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 de06d5d1287..1afdf45db38 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -65,11 +65,26 @@  #define REG_DBG_PRINT(args...)  #endif +/** + * enum reg_request_treatment - regulatory request treatment + * + * @REG_REQ_OK: continue processing the regulatory request + * @REG_REQ_IGNORE: ignore the regulatory request + * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should + *	be intersected with the current one. + * @REG_REQ_ALREADY_SET: the regulatory request will not change the current + *	regulatory settings, and no further processing is required. + * @REG_REQ_USER_HINT_HANDLED: a non alpha2  user hint was handled and no + *	further processing is required, i.e., not need to update last_request + *	etc. This should be used for user hints that do not provide an alpha2 + *	but some other type of regulatory hint, i.e., indoor operation. + */  enum reg_request_treatment {  	REG_REQ_OK,  	REG_REQ_IGNORE,  	REG_REQ_INTERSECT,  	REG_REQ_ALREADY_SET, +	REG_REQ_USER_HINT_HANDLED,  };  static struct regulatory_request core_request_world = { @@ -91,10 +106,6 @@ static struct regulatory_request __rcu *last_request =  /* To trigger userspace events */  static struct platform_device *reg_pdev; -static struct device_type reg_device_type = { -	.uevent = reg_device_uevent, -}; -  /*   * Central wireless core regulatory domains, we only need two,   * the current one and a world regulatory domain in case we have no @@ -110,6 +121,14 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain;   */  static int reg_num_devs_support_basehint; +/* + * State variable indicating if the platform on which the devices + * are attached is operating in an indoor environment. The state variable + * is relevant for all registered devices. + * (protected by RTNL) + */ +static bool reg_is_indoor; +  static const struct ieee80211_regdomain *get_cfg80211_regdom(void)  {  	return rtnl_dereference(cfg80211_regdomain); @@ -120,6 +139,48 @@ static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)  	return rtnl_dereference(wiphy->regd);  } +static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region) +{ +	switch (dfs_region) { +	case NL80211_DFS_UNSET: +		return "unset"; +	case NL80211_DFS_FCC: +		return "FCC"; +	case NL80211_DFS_ETSI: +		return "ETSI"; +	case NL80211_DFS_JP: +		return "JP"; +	} +	return "Unknown"; +} + +enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy) +{ +	const struct ieee80211_regdomain *regd = NULL; +	const struct ieee80211_regdomain *wiphy_regd = NULL; + +	regd = get_cfg80211_regdom(); +	if (!wiphy) +		goto out; + +	wiphy_regd = get_wiphy_regdom(wiphy); +	if (!wiphy_regd) +		goto out; + +	if (wiphy_regd->dfs_region == regd->dfs_region) +		goto out; + +	REG_DBG_PRINT("%s: device specific dfs_region " +		      "(%s) disagrees with cfg80211's " +		      "central dfs_region (%s)\n", +		      dev_name(&wiphy->dev), +		      reg_dfs_region_str(wiphy_regd->dfs_region), +		      reg_dfs_region_str(regd->dfs_region)); + +out: +	return regd->dfs_region; +} +  static void rcu_free_regdom(const struct ieee80211_regdomain *r)  {  	if (!r) @@ -163,25 +224,29 @@ static const struct ieee80211_regdomain world_regdom = {  		REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),  		/* IEEE 802.11b/g, channels 12..13. */  		REG_RULE(2467-10, 2472+10, 40, 6, 20, -			NL80211_RRF_PASSIVE_SCAN | -			NL80211_RRF_NO_IBSS), +			NL80211_RRF_NO_IR),  		/* IEEE 802.11 channel 14 - Only JP enables  		 * this and for 802.11b only */  		REG_RULE(2484-10, 2484+10, 20, 6, 20, -			NL80211_RRF_PASSIVE_SCAN | -			NL80211_RRF_NO_IBSS | +			NL80211_RRF_NO_IR |  			NL80211_RRF_NO_OFDM),  		/* IEEE 802.11a, channel 36..48 */ -		REG_RULE(5180-10, 5240+10, 80, 6, 20, -                        NL80211_RRF_PASSIVE_SCAN | -                        NL80211_RRF_NO_IBSS), +		REG_RULE(5180-10, 5240+10, 160, 6, 20, +                        NL80211_RRF_NO_IR), -		/* NB: 5260 MHz - 5700 MHz requires DFS */ +		/* IEEE 802.11a, channel 52..64 - DFS required */ +		REG_RULE(5260-10, 5320+10, 160, 6, 20, +			NL80211_RRF_NO_IR | +			NL80211_RRF_DFS), + +		/* IEEE 802.11a, channel 100..144 - DFS required */ +		REG_RULE(5500-10, 5720+10, 160, 6, 20, +			NL80211_RRF_NO_IR | +			NL80211_RRF_DFS),  		/* IEEE 802.11a, channel 149..165 */  		REG_RULE(5745-10, 5825+10, 80, 6, 20, -			NL80211_RRF_PASSIVE_SCAN | -			NL80211_RRF_NO_IBSS), +			NL80211_RRF_NO_IR),  		/* IEEE 802.11ad (60gHz), channels 1..3 */  		REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0), @@ -198,11 +263,36 @@ static char user_alpha2[2];  module_param(ieee80211_regdom, charp, 0444);  MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); +static void reg_free_request(struct regulatory_request *request) +{ +	if (request != get_last_request()) +		kfree(request); +} + +static void reg_free_last_request(void) +{ +	struct regulatory_request *lr = get_last_request(); + +	if (lr != &core_request_world && lr) +		kfree_rcu(lr, rcu_head); +} + +static void reg_update_last_request(struct regulatory_request *request) +{ +	struct regulatory_request *lr; + +	lr = get_last_request(); +	if (lr == request) +		return; + +	reg_free_last_request(); +	rcu_assign_pointer(last_request, request); +} +  static void reset_regdomains(bool full_reset,  			     const struct ieee80211_regdomain *new_regdom)  {  	const struct ieee80211_regdomain *r; -	struct regulatory_request *lr;  	ASSERT_RTNL(); @@ -225,10 +315,7 @@ static void reset_regdomains(bool full_reset,  	if (!full_reset)  		return; -	lr = get_last_request(); -	if (lr != &core_request_world && lr) -		kfree_rcu(lr, rcu_head); -	rcu_assign_pointer(last_request, &core_request_world); +	reg_update_last_request(&core_request_world);  }  /* @@ -429,11 +516,16 @@ static inline void reg_regdb_query(const char *alpha2) {}  /*   * This lets us keep regulatory code which is updated on a regulatory - * basis in userspace. Country information is filled in by - * reg_device_uevent + * basis in userspace.   */  static int call_crda(const char *alpha2)  { +	char country[12]; +	char *env[] = { country, NULL }; + +	snprintf(country, sizeof(country), "COUNTRY=%c%c", +		 alpha2[0], alpha2[1]); +  	if (!is_world_regdom((char *) alpha2))  		pr_info("Calling CRDA for country: %c%c\n",  			alpha2[0], alpha2[1]); @@ -443,10 +535,18 @@ static int call_crda(const char *alpha2)  	/* query internal regulatory database (if it exists) */  	reg_regdb_query(alpha2); -	return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); +	return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env); +} + +static enum reg_request_treatment +reg_call_crda(struct regulatory_request *request) +{ +	if (call_crda(request->alpha2)) +		return REG_REQ_IGNORE; +	return REG_REQ_OK;  } -static bool reg_is_valid_request(const char *alpha2) +bool reg_is_valid_request(const char *alpha2)  {  	struct regulatory_request *lr = get_last_request(); @@ -456,6 +556,71 @@ static bool reg_is_valid_request(const char *alpha2)  	return alpha2_equal(lr->alpha2, alpha2);  } +static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy) +{ +	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) +		return get_wiphy_regdom(wiphy); + +	return get_cfg80211_regdom(); +} + +unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, +				   const struct ieee80211_reg_rule *rule) +{ +	const struct ieee80211_freq_range *freq_range = &rule->freq_range; +	const struct ieee80211_freq_range *freq_range_tmp; +	const struct ieee80211_reg_rule *tmp; +	u32 start_freq, end_freq, idx, no; + +	for (idx = 0; idx < rd->n_reg_rules; idx++) +		if (rule == &rd->reg_rules[idx]) +			break; + +	if (idx == rd->n_reg_rules) +		return 0; + +	/* get start_freq */ +	no = idx; + +	while (no) { +		tmp = &rd->reg_rules[--no]; +		freq_range_tmp = &tmp->freq_range; + +		if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz) +			break; + +		freq_range = freq_range_tmp; +	} + +	start_freq = freq_range->start_freq_khz; + +	/* get end_freq */ +	freq_range = &rule->freq_range; +	no = idx; + +	while (no < rd->n_reg_rules - 1) { +		tmp = &rd->reg_rules[++no]; +		freq_range_tmp = &tmp->freq_range; + +		if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz) +			break; + +		freq_range = freq_range_tmp; +	} + +	end_freq = freq_range->end_freq_khz; + +	return end_freq - start_freq; +} +  /* Sanity check on a regulatory rule */  static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)  { @@ -547,10 +712,26 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,  }  /* + * Later on we can perhaps use the more restrictive DFS + * region but we don't have information for that yet so + * for now simply disallow conflicts. + */ +static enum nl80211_dfs_regions +reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1, +			 const enum nl80211_dfs_regions dfs_region2) +{ +	if (dfs_region1 != dfs_region2) +		return NL80211_DFS_UNSET; +	return dfs_region1; +} + +/*   * Helper for regdom_intersect(), this does the real   * mathematical intersection fun   */ -static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, +static int reg_rules_intersect(const struct ieee80211_regdomain *rd1, +			       const struct ieee80211_regdomain *rd2, +			       const struct ieee80211_reg_rule *rule1,  			       const struct ieee80211_reg_rule *rule2,  			       struct ieee80211_reg_rule *intersected_rule)  { @@ -558,7 +739,7 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,  	struct ieee80211_freq_range *freq_range;  	const struct ieee80211_power_rule *power_rule1, *power_rule2;  	struct ieee80211_power_rule *power_rule; -	u32 freq_diff; +	u32 freq_diff, max_bandwidth1, max_bandwidth2;  	freq_range1 = &rule1->freq_range;  	freq_range2 = &rule2->freq_range; @@ -572,8 +753,32 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,  					 freq_range2->start_freq_khz);  	freq_range->end_freq_khz = min(freq_range1->end_freq_khz,  				       freq_range2->end_freq_khz); -	freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, -					    freq_range2->max_bandwidth_khz); + +	max_bandwidth1 = freq_range1->max_bandwidth_khz; +	max_bandwidth2 = freq_range2->max_bandwidth_khz; + +	if (rule1->flags & NL80211_RRF_AUTO_BW) +		max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1); +	if (rule2->flags & NL80211_RRF_AUTO_BW) +		max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2); + +	freq_range->max_bandwidth_khz = min(max_bandwidth1, max_bandwidth2); + +	intersected_rule->flags = rule1->flags | rule2->flags; + +	/* +	 * In case NL80211_RRF_AUTO_BW requested for both rules +	 * set AUTO_BW in intersected rule also. Next we will +	 * calculate BW correctly in handle_channel function. +	 * In other case remove AUTO_BW flag while we calculate +	 * maximum bandwidth correctly and auto calculation is +	 * not required. +	 */ +	if ((rule1->flags & NL80211_RRF_AUTO_BW) && +	    (rule2->flags & NL80211_RRF_AUTO_BW)) +		intersected_rule->flags |= NL80211_RRF_AUTO_BW; +	else +		intersected_rule->flags &= ~NL80211_RRF_AUTO_BW;  	freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;  	if (freq_range->max_bandwidth_khz > freq_diff) @@ -584,7 +789,8 @@ static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1,  	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->dfs_cac_ms = max(rule1->dfs_cac_ms, +					   rule2->dfs_cac_ms);  	if (!is_valid_reg_rule(intersected_rule))  		return -EINVAL; @@ -633,7 +839,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,  		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, &dummy_rule)) +			if (!reg_rules_intersect(rd1, rd2, rule1, rule2, +						 &dummy_rule))  				num_rules++;  		}  	} @@ -658,7 +865,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,  			 * a memcpy()  			 */  			intersected_rule = &rd->reg_rules[rule_idx]; -			r = reg_rules_intersect(rule1, rule2, intersected_rule); +			r = reg_rules_intersect(rd1, rd2, rule1, rule2, +						intersected_rule);  			/*  			 * No need to memset here the intersected rule here as  			 * we're not using the stack anymore @@ -677,6 +885,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,  	rd->n_reg_rules = num_rules;  	rd->alpha2[0] = '9';  	rd->alpha2[1] = '8'; +	rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region, +						  rd2->dfs_region);  	return rd;  } @@ -688,14 +898,14 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,  static u32 map_regdom_flags(u32 rd_flags)  {  	u32 channel_flags = 0; -	if (rd_flags & NL80211_RRF_PASSIVE_SCAN) -		channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; -	if (rd_flags & NL80211_RRF_NO_IBSS) -		channel_flags |= IEEE80211_CHAN_NO_IBSS; +	if (rd_flags & NL80211_RRF_NO_IR_ALL) +		channel_flags |= IEEE80211_CHAN_NO_IR;  	if (rd_flags & NL80211_RRF_DFS)  		channel_flags |= IEEE80211_CHAN_RADAR;  	if (rd_flags & NL80211_RRF_NO_OFDM)  		channel_flags |= IEEE80211_CHAN_NO_OFDM; +	if (rd_flags & NL80211_RRF_NO_OUTDOOR) +		channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;  	return channel_flags;  } @@ -741,66 +951,68 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,  					       u32 center_freq)  {  	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(); +	regd = reg_get_regdomain(wiphy);  	return freq_reg_info_regd(wiphy, center_freq, regd);  }  EXPORT_SYMBOL(freq_reg_info); -#ifdef CONFIG_CFG80211_REG_DEBUG -static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) +const char *reg_initiator_name(enum nl80211_reg_initiator initiator)  {  	switch (initiator) {  	case NL80211_REGDOM_SET_BY_CORE: -		return "Set by core"; +		return "core";  	case NL80211_REGDOM_SET_BY_USER: -		return "Set by user"; +		return "user";  	case NL80211_REGDOM_SET_BY_DRIVER: -		return "Set by driver"; +		return "driver";  	case NL80211_REGDOM_SET_BY_COUNTRY_IE: -		return "Set by country IE"; +		return "country IE";  	default:  		WARN_ON(1); -		return "Set by bug"; +		return "bug";  	}  } +EXPORT_SYMBOL(reg_initiator_name); -static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, +#ifdef CONFIG_CFG80211_REG_DEBUG +static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd, +				    struct ieee80211_channel *chan,  				    const struct ieee80211_reg_rule *reg_rule)  {  	const struct ieee80211_power_rule *power_rule;  	const struct ieee80211_freq_range *freq_range; -	char max_antenna_gain[32]; +	char max_antenna_gain[32], bw[32];  	power_rule = ®_rule->power_rule;  	freq_range = ®_rule->freq_range;  	if (!power_rule->max_antenna_gain) -		snprintf(max_antenna_gain, 32, "N/A"); +		snprintf(max_antenna_gain, sizeof(max_antenna_gain), "N/A");  	else -		snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); +		snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d", +			 power_rule->max_antenna_gain); + +	if (reg_rule->flags & NL80211_RRF_AUTO_BW) +		snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO", +			 freq_range->max_bandwidth_khz, +			 reg_get_max_bandwidth(regd, reg_rule)); +	else +		snprintf(bw, sizeof(bw), "%d KHz", +			 freq_range->max_bandwidth_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", +	REG_DBG_PRINT("%d KHz - %d KHz @ %s), (%s mBi, %d mBm)\n",  		      freq_range->start_freq_khz, freq_range->end_freq_khz, -		      freq_range->max_bandwidth_khz, max_antenna_gain, +		      bw, max_antenna_gain,  		      power_rule->max_eirp);  }  #else -static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, +static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd, +				    struct ieee80211_channel *chan,  				    const struct ieee80211_reg_rule *reg_rule)  {  	return; @@ -822,6 +1034,8 @@ static void handle_channel(struct wiphy *wiphy,  	const struct ieee80211_freq_range *freq_range = NULL;  	struct wiphy *request_wiphy = NULL;  	struct regulatory_request *lr = get_last_request(); +	const struct ieee80211_regdomain *regd; +	u32 max_bandwidth_khz;  	request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); @@ -843,26 +1057,42 @@ static void handle_channel(struct wiphy *wiphy,  		    PTR_ERR(reg_rule) == -ERANGE)  			return; -		REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); -		chan->flags |= IEEE80211_CHAN_DISABLED; +		if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && +		    request_wiphy && request_wiphy == wiphy && +		    request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { +			REG_DBG_PRINT("Disabling freq %d MHz for good\n", +				      chan->center_freq); +			chan->orig_flags |= IEEE80211_CHAN_DISABLED; +			chan->flags = chan->orig_flags; +		} else { +			REG_DBG_PRINT("Disabling freq %d MHz\n", +				      chan->center_freq); +			chan->flags |= IEEE80211_CHAN_DISABLED; +		}  		return;  	} -	chan_reg_rule_print_dbg(chan, reg_rule); +	regd = reg_get_regdomain(wiphy); +	chan_reg_rule_print_dbg(regd, chan, reg_rule);  	power_rule = ®_rule->power_rule;  	freq_range = ®_rule->freq_range; -	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) +	max_bandwidth_khz = freq_range->max_bandwidth_khz; +	/* Check if auto calculation requested */ +	if (reg_rule->flags & NL80211_RRF_AUTO_BW) +		max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); + +	if (max_bandwidth_khz < MHZ_TO_KHZ(40))  		bw_flags = IEEE80211_CHAN_NO_HT40; -	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) +	if (max_bandwidth_khz < MHZ_TO_KHZ(80))  		bw_flags |= IEEE80211_CHAN_NO_80MHZ; -	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) +	if (max_bandwidth_khz < MHZ_TO_KHZ(160))  		bw_flags |= IEEE80211_CHAN_NO_160MHZ;  	if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&  	    request_wiphy && request_wiphy == wiphy && -	    request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { +	    request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {  		/*  		 * This guarantees the driver's requested regulatory domain  		 * will always be used as a base for further regulatory @@ -874,6 +1104,13 @@ static void handle_channel(struct wiphy *wiphy,  			(int) MBI_TO_DBI(power_rule->max_antenna_gain);  		chan->max_reg_power = chan->max_power = chan->orig_mpwr =  			(int) MBM_TO_DBM(power_rule->max_eirp); + +		if (chan->flags & IEEE80211_CHAN_RADAR) { +			chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; +			if (reg_rule->dfs_cac_ms) +				chan->dfs_cac_ms = reg_rule->dfs_cac_ms; +		} +  		return;  	} @@ -886,15 +1123,21 @@ static void handle_channel(struct wiphy *wiphy,  		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->flags & IEEE80211_CHAN_RADAR) { +		if (reg_rule->dfs_cac_ms) +			chan->dfs_cac_ms = reg_rule->dfs_cac_ms; +		else +			chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; +	} +  	if (chan->orig_mpwr) {  		/* -		 * Devices that have their own custom regulatory domain -		 * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the -		 * passed country IE power settings. +		 * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER +		 * will always follow the passed country IE power settings.  		 */  		if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && -		    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && -		    wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) +		    wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER)  			chan->max_power = chan->max_reg_power;  		else  			chan->max_power = min(chan->orig_mpwr, @@ -923,12 +1166,19 @@ static bool reg_request_cell_base(struct regulatory_request *request)  	return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;  } +static bool reg_request_indoor(struct regulatory_request *request) +{ +	if (request->initiator != NL80211_REGDOM_SET_BY_USER) +		return false; +	return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR; +} +  bool reg_last_request_cell_base(void)  {  	return reg_request_cell_base(get_last_request());  } -#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS +#ifdef CONFIG_CFG80211_REG_CELLULAR_HINTS  /* Core specific check */  static enum reg_request_treatment  reg_ignore_cell_hint(struct regulatory_request *pending_request) @@ -962,6 +1212,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)  }  #endif +static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy) +{ +	if (wiphy->regulatory_flags & REGULATORY_STRICT_REG && +	    !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)) +		return true; +	return false; +}  static bool ignore_reg_update(struct wiphy *wiphy,  			      enum nl80211_reg_initiator initiator) @@ -969,14 +1226,17 @@ static bool ignore_reg_update(struct wiphy *wiphy,  	struct regulatory_request *lr = get_last_request();  	if (!lr) { -		REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", +		REG_DBG_PRINT("Ignoring regulatory request set by %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", +	    wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { +		REG_DBG_PRINT("Ignoring regulatory request set by %s " +			      "since the driver uses its own custom " +			      "regulatory domain\n",  			      reg_initiator_name(initiator));  		return true;  	} @@ -985,10 +1245,12 @@ static bool ignore_reg_update(struct wiphy *wiphy,  	 * wiphy->regd will be set once the device has its own  	 * desired regulatory domain set  	 */ -	if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && +	if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&  	    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&  	    !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_DBG_PRINT("Ignoring regulatory request set by %s " +			      "since the driver requires its own regulatory " +			      "domain to be set first\n",  			      reg_initiator_name(initiator));  		return true;  	} @@ -1009,7 +1271,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy)  		return true;  	if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && -	    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) +	    wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)  		return true;  	return false; @@ -1037,19 +1299,14 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,  	if (!reg_is_world_roaming(wiphy))  		return; -	if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) +	if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS)  		return;  	chan_before.center_freq = chan->center_freq;  	chan_before.flags = chan->flags; -	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { -		chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; -		channel_changed = true; -	} - -	if (chan->flags & IEEE80211_CHAN_NO_IBSS) { -		chan->flags &= ~IEEE80211_CHAN_NO_IBSS; +	if (chan->flags & IEEE80211_CHAN_NO_IR) { +		chan->flags &= ~IEEE80211_CHAN_NO_IR;  		channel_changed = true;  	} @@ -1182,14 +1439,30 @@ static void reg_process_ht_flags(struct wiphy *wiphy)  		reg_process_ht_flags_band(wiphy, wiphy->bands[band]);  } +static void reg_call_notifier(struct wiphy *wiphy, +			      struct regulatory_request *request) +{ +	if (wiphy->reg_notifier) +		wiphy->reg_notifier(wiphy, request); +} +  static void wiphy_update_regulatory(struct wiphy *wiphy,  				    enum nl80211_reg_initiator initiator)  {  	enum ieee80211_band band;  	struct regulatory_request *lr = get_last_request(); -	if (ignore_reg_update(wiphy, initiator)) +	if (ignore_reg_update(wiphy, initiator)) { +		/* +		 * Regulatory updates set by CORE are ignored for custom +		 * regulatory cards. Let us notify the changes to the driver, +		 * as some drivers used this to restore its orig_* reg domain. +		 */ +		if (initiator == NL80211_REGDOM_SET_BY_CORE && +		    wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) +			reg_call_notifier(wiphy, lr);  		return; +	}  	lr->dfs_region = get_cfg80211_regdom()->dfs_region; @@ -1198,9 +1471,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,  	reg_process_beacons(wiphy);  	reg_process_ht_flags(wiphy); - -	if (wiphy->reg_notifier) -		wiphy->reg_notifier(wiphy, lr); +	reg_call_notifier(wiphy, lr);  }  static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) @@ -1213,15 +1484,6 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {  		wiphy = &rdev->wiphy;  		wiphy_update_regulatory(wiphy, initiator); -		/* -		 * Regulatory updates set by CORE are ignored for custom -		 * regulatory cards. Let us notify the changes to the driver, -		 * as some drivers used this to restore its orig_* reg domain. -		 */ -		if (initiator == NL80211_REGDOM_SET_BY_CORE && -		    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && -		    wiphy->reg_notifier) -			wiphy->reg_notifier(wiphy, get_last_request());  	}  } @@ -1233,6 +1495,7 @@ static void handle_channel_custom(struct wiphy *wiphy,  	const struct ieee80211_reg_rule *reg_rule = NULL;  	const struct ieee80211_power_rule *power_rule = NULL;  	const struct ieee80211_freq_range *freq_range = NULL; +	u32 max_bandwidth_khz;  	reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),  				      regd); @@ -1240,20 +1503,26 @@ static void handle_channel_custom(struct wiphy *wiphy,  	if (IS_ERR(reg_rule)) {  		REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",  			      chan->center_freq); -		chan->flags = IEEE80211_CHAN_DISABLED; +		chan->orig_flags |= IEEE80211_CHAN_DISABLED; +		chan->flags = chan->orig_flags;  		return;  	} -	chan_reg_rule_print_dbg(chan, reg_rule); +	chan_reg_rule_print_dbg(regd, chan, reg_rule);  	power_rule = ®_rule->power_rule;  	freq_range = ®_rule->freq_range; -	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) +	max_bandwidth_khz = freq_range->max_bandwidth_khz; +	/* Check if auto calculation requested */ +	if (reg_rule->flags & NL80211_RRF_AUTO_BW) +		max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); + +	if (max_bandwidth_khz < MHZ_TO_KHZ(40))  		bw_flags = IEEE80211_CHAN_NO_HT40; -	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) +	if (max_bandwidth_khz < MHZ_TO_KHZ(80))  		bw_flags |= IEEE80211_CHAN_NO_80MHZ; -	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) +	if (max_bandwidth_khz < MHZ_TO_KHZ(160))  		bw_flags |= IEEE80211_CHAN_NO_160MHZ;  	chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; @@ -1282,6 +1551,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,  	enum ieee80211_band band;  	unsigned int bands_set = 0; +	WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG), +	     "wiphy should have REGULATORY_CUSTOM_REG\n"); +	wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; +  	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {  		if (!wiphy->bands[band])  			continue; @@ -1297,249 +1570,334 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,  }  EXPORT_SYMBOL(wiphy_apply_custom_regulatory); -/* This has the logic which determines when a new request - * should be ignored. */ -static enum reg_request_treatment -get_reg_request_treatment(struct wiphy *wiphy, -			  struct regulatory_request *pending_request) +static void reg_set_request_processed(void)  { -	struct wiphy *last_wiphy = NULL; +	bool need_more_processing = false;  	struct regulatory_request *lr = get_last_request(); -	/* All initial requests are respected */ -	if (!lr) -		return REG_REQ_OK; +	lr->processed = true; -	switch (pending_request->initiator) { -	case NL80211_REGDOM_SET_BY_CORE: -		return REG_REQ_OK; -	case NL80211_REGDOM_SET_BY_COUNTRY_IE: -		if (reg_request_cell_base(lr)) { -			/* Trust a Cell base station over the AP's country IE */ -			if (regdom_changes(pending_request->alpha2)) -				return REG_REQ_IGNORE; -			return REG_REQ_ALREADY_SET; -		} +	spin_lock(®_requests_lock); +	if (!list_empty(®_requests_list)) +		need_more_processing = true; +	spin_unlock(®_requests_lock); -		last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); +	if (lr->initiator == NL80211_REGDOM_SET_BY_USER) +		cancel_delayed_work(®_timeout); -		if (unlikely(!is_an_alpha2(pending_request->alpha2))) -			return -EINVAL; -		if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { -			if (last_wiphy != wiphy) { -				/* -				 * Two cards with two APs claiming different -				 * Country IE alpha2s. We could -				 * intersect them, but that seems unlikely -				 * to be correct. Reject second one for now. -				 */ -				if (regdom_changes(pending_request->alpha2)) -					return REG_REQ_IGNORE; -				return REG_REQ_ALREADY_SET; -			} -			/* -			 * Two consecutive Country IE hints on the same wiphy. -			 * This should be picked up early by the driver/stack -			 */ -			if (WARN_ON(regdom_changes(pending_request->alpha2))) -				return REG_REQ_OK; -			return REG_REQ_ALREADY_SET; -		} -		return REG_REQ_OK; -	case NL80211_REGDOM_SET_BY_DRIVER: -		if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { -			if (regdom_changes(pending_request->alpha2)) -				return REG_REQ_OK; -			return REG_REQ_ALREADY_SET; -		} +	if (need_more_processing) +		schedule_work(®_work); +} -		/* -		 * This would happen if you unplug and plug your card -		 * back in or if you add a new device for which the previously -		 * loaded card also agrees on the regulatory domain. -		 */ -		if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && -		    !regdom_changes(pending_request->alpha2)) -			return REG_REQ_ALREADY_SET; +/** + * reg_process_hint_core - process core regulatory requests + * @pending_request: a pending core regulatory request + * + * The wireless subsystem can use this function to process + * a regulatory request issued by the regulatory core. + * + * Returns one of the different reg request treatment values. + */ +static enum reg_request_treatment +reg_process_hint_core(struct regulatory_request *core_request) +{ + +	core_request->intersect = false; +	core_request->processed = false; +	reg_update_last_request(core_request); + +	return reg_call_crda(core_request); +} + +static enum reg_request_treatment +__reg_process_hint_user(struct regulatory_request *user_request) +{ +	struct regulatory_request *lr = get_last_request(); + +	if (reg_request_indoor(user_request)) { +		reg_is_indoor = true; +		return REG_REQ_USER_HINT_HANDLED; +	} + +	if (reg_request_cell_base(user_request)) +		return reg_ignore_cell_hint(user_request); + +	if (reg_request_cell_base(lr)) +		return REG_REQ_IGNORE; + +	if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)  		return REG_REQ_INTERSECT; -	case NL80211_REGDOM_SET_BY_USER: -		if (reg_request_cell_base(pending_request)) -			return reg_ignore_cell_hint(pending_request); +	/* +	 * If the user knows better the user should set the regdom +	 * to their country before the IE is picked up +	 */ +	if (lr->initiator == NL80211_REGDOM_SET_BY_USER && +	    lr->intersect) +		return REG_REQ_IGNORE; +	/* +	 * Process user requests only after previous user/driver/core +	 * requests have been processed +	 */ +	if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || +	     lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || +	     lr->initiator == NL80211_REGDOM_SET_BY_USER) && +	    regdom_changes(lr->alpha2)) +		return REG_REQ_IGNORE; -		if (reg_request_cell_base(lr)) -			return REG_REQ_IGNORE; +	if (!regdom_changes(user_request->alpha2)) +		return REG_REQ_ALREADY_SET; -		if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) -			return REG_REQ_INTERSECT; -		/* -		 * If the user knows better the user should set the regdom -		 * to their country before the IE is picked up -		 */ -		if (lr->initiator == NL80211_REGDOM_SET_BY_USER && -		    lr->intersect) -			return REG_REQ_IGNORE; -		/* -		 * Process user requests only after previous user/driver/core -		 * requests have been processed -		 */ -		if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || -		     lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || -		     lr->initiator == NL80211_REGDOM_SET_BY_USER) && -		    regdom_changes(lr->alpha2)) -			return REG_REQ_IGNORE; +	return REG_REQ_OK; +} -		if (!regdom_changes(pending_request->alpha2)) -			return REG_REQ_ALREADY_SET; +/** + * reg_process_hint_user - process user regulatory requests + * @user_request: a pending user regulatory request + * + * The wireless subsystem can use this function to process + * a regulatory request initiated by userspace. + * + * Returns one of the different reg request treatment values. + */ +static enum reg_request_treatment +reg_process_hint_user(struct regulatory_request *user_request) +{ +	enum reg_request_treatment treatment; -		return REG_REQ_OK; +	treatment = __reg_process_hint_user(user_request); +	if (treatment == REG_REQ_IGNORE || +	    treatment == REG_REQ_ALREADY_SET || +	    treatment == REG_REQ_USER_HINT_HANDLED) { +		reg_free_request(user_request); +		return treatment;  	} -	return REG_REQ_IGNORE; +	user_request->intersect = treatment == REG_REQ_INTERSECT; +	user_request->processed = false; + +	reg_update_last_request(user_request); + +	user_alpha2[0] = user_request->alpha2[0]; +	user_alpha2[1] = user_request->alpha2[1]; + +	return reg_call_crda(user_request);  } -static void reg_set_request_processed(void) +static enum reg_request_treatment +__reg_process_hint_driver(struct regulatory_request *driver_request)  { -	bool need_more_processing = false;  	struct regulatory_request *lr = get_last_request(); -	lr->processed = true; - -	spin_lock(®_requests_lock); -	if (!list_empty(®_requests_list)) -		need_more_processing = true; -	spin_unlock(®_requests_lock); +	if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { +		if (regdom_changes(driver_request->alpha2)) +			return REG_REQ_OK; +		return REG_REQ_ALREADY_SET; +	} -	if (lr->initiator == NL80211_REGDOM_SET_BY_USER) -		cancel_delayed_work(®_timeout); +	/* +	 * This would happen if you unplug and plug your card +	 * back in or if you add a new device for which the previously +	 * loaded card also agrees on the regulatory domain. +	 */ +	if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && +	    !regdom_changes(driver_request->alpha2)) +		return REG_REQ_ALREADY_SET; -	if (need_more_processing) -		schedule_work(®_work); +	return REG_REQ_INTERSECT;  }  /** - * __regulatory_hint - hint to the wireless core a regulatory domain - * @wiphy: if the hint comes from country information from an AP, this - *	is required to be set to the wiphy that received the information - * @pending_request: the regulatory request currently being processed + * reg_process_hint_driver - process driver regulatory requests + * @driver_request: a pending driver regulatory request   * - * The Wireless subsystem can use this function to hint to the wireless core - * what it believes should be the current regulatory domain. + * The wireless subsystem can use this function to process + * a regulatory request issued by an 802.11 driver.   *   * Returns one of the different reg request treatment values.   */  static enum reg_request_treatment -__regulatory_hint(struct wiphy *wiphy, -		  struct regulatory_request *pending_request) +reg_process_hint_driver(struct wiphy *wiphy, +			struct regulatory_request *driver_request)  {  	const struct ieee80211_regdomain *regd; -	bool intersect = false;  	enum reg_request_treatment treatment; -	struct regulatory_request *lr; -	treatment = get_reg_request_treatment(wiphy, pending_request); +	treatment = __reg_process_hint_driver(driver_request);  	switch (treatment) { -	case REG_REQ_INTERSECT: -		if (pending_request->initiator == -		    NL80211_REGDOM_SET_BY_DRIVER) { -			regd = reg_copy_regd(get_cfg80211_regdom()); -			if (IS_ERR(regd)) { -				kfree(pending_request); -				return PTR_ERR(regd); -			} -			rcu_assign_pointer(wiphy->regd, regd); -		} -		intersect = true; -		break;  	case REG_REQ_OK:  		break; -	default: -		/* -		 * If the regulatory domain being requested by the -		 * driver has already been set just copy it to the -		 * wiphy -		 */ -		if (treatment == REG_REQ_ALREADY_SET && -		    pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { -			regd = reg_copy_regd(get_cfg80211_regdom()); -			if (IS_ERR(regd)) { -				kfree(pending_request); -				return REG_REQ_IGNORE; -			} -			treatment = REG_REQ_ALREADY_SET; -			rcu_assign_pointer(wiphy->regd, regd); -			goto new_request; -		} -		kfree(pending_request); +	case REG_REQ_IGNORE: +	case REG_REQ_USER_HINT_HANDLED: +		reg_free_request(driver_request);  		return treatment; +	case REG_REQ_INTERSECT: +		/* fall through */ +	case REG_REQ_ALREADY_SET: +		regd = reg_copy_regd(get_cfg80211_regdom()); +		if (IS_ERR(regd)) { +			reg_free_request(driver_request); +			return REG_REQ_IGNORE; +		} +		rcu_assign_pointer(wiphy->regd, regd);  	} -new_request: -	lr = get_last_request(); -	if (lr != &core_request_world && lr) -		kfree_rcu(lr, rcu_head); -	pending_request->intersect = intersect; -	pending_request->processed = false; -	rcu_assign_pointer(last_request, pending_request); -	lr = pending_request; +	driver_request->intersect = treatment == REG_REQ_INTERSECT; +	driver_request->processed = false; -	pending_request = NULL; +	reg_update_last_request(driver_request); -	if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { -		user_alpha2[0] = lr->alpha2[0]; -		user_alpha2[1] = lr->alpha2[1]; +	/* +	 * Since CRDA will not be called in this case as we already +	 * have applied the requested regulatory domain before we just +	 * inform userspace we have processed the request +	 */ +	if (treatment == REG_REQ_ALREADY_SET) { +		nl80211_send_reg_change_event(driver_request); +		reg_set_request_processed(); +		return treatment;  	} -	/* When r == REG_REQ_INTERSECT we do need to call CRDA */ -	if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { +	return reg_call_crda(driver_request); +} + +static enum reg_request_treatment +__reg_process_hint_country_ie(struct wiphy *wiphy, +			      struct regulatory_request *country_ie_request) +{ +	struct wiphy *last_wiphy = NULL; +	struct regulatory_request *lr = get_last_request(); + +	if (reg_request_cell_base(lr)) { +		/* Trust a Cell base station over the AP's country IE */ +		if (regdom_changes(country_ie_request->alpha2)) +			return REG_REQ_IGNORE; +		return REG_REQ_ALREADY_SET; +	} else { +		if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE) +			return REG_REQ_IGNORE; +	} + +	if (unlikely(!is_an_alpha2(country_ie_request->alpha2))) +		return -EINVAL; + +	if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) +		return REG_REQ_OK; + +	last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); + +	if (last_wiphy != wiphy) {  		/* -		 * Since CRDA will not be called in this case as we already -		 * have applied the requested regulatory domain before we just -		 * inform userspace we have processed the request +		 * Two cards with two APs claiming different +		 * Country IE alpha2s. We could +		 * intersect them, but that seems unlikely +		 * to be correct. Reject second one for now.  		 */ -		if (treatment == REG_REQ_ALREADY_SET) { -			nl80211_send_reg_change_event(lr); -			reg_set_request_processed(); -		} -		return treatment; +		if (regdom_changes(country_ie_request->alpha2)) +			return REG_REQ_IGNORE; +		return REG_REQ_ALREADY_SET;  	} +	/* +	 * Two consecutive Country IE hints on the same wiphy. +	 * This should be picked up early by the driver/stack +	 */ +	if (WARN_ON(regdom_changes(country_ie_request->alpha2))) +		return REG_REQ_OK; +	return REG_REQ_ALREADY_SET; +} + +/** + * reg_process_hint_country_ie - process regulatory requests from country IEs + * @country_ie_request: a regulatory request from a country IE + * + * The wireless subsystem can use this function to process + * a regulatory request issued by a country Information Element. + * + * Returns one of the different reg request treatment values. + */ +static enum reg_request_treatment +reg_process_hint_country_ie(struct wiphy *wiphy, +			    struct regulatory_request *country_ie_request) +{ +	enum reg_request_treatment treatment; + +	treatment = __reg_process_hint_country_ie(wiphy, country_ie_request); -	if (call_crda(lr->alpha2)) +	switch (treatment) { +	case REG_REQ_OK: +		break; +	case REG_REQ_IGNORE: +	case REG_REQ_USER_HINT_HANDLED: +		/* fall through */ +	case REG_REQ_ALREADY_SET: +		reg_free_request(country_ie_request); +		return treatment; +	case REG_REQ_INTERSECT: +		reg_free_request(country_ie_request); +		/* +		 * This doesn't happen yet, not sure we +		 * ever want to support it for this case. +		 */ +		WARN_ONCE(1, "Unexpected intersection for country IEs");  		return REG_REQ_IGNORE; -	return REG_REQ_OK; +	} + +	country_ie_request->intersect = false; +	country_ie_request->processed = false; + +	reg_update_last_request(country_ie_request); + +	return reg_call_crda(country_ie_request);  }  /* This processes *all* regulatory hints */ -static void reg_process_hint(struct regulatory_request *reg_request, -			     enum nl80211_reg_initiator reg_initiator) +static void reg_process_hint(struct regulatory_request *reg_request)  {  	struct wiphy *wiphy = NULL; - -	if (WARN_ON(!reg_request->alpha2)) -		return; +	enum reg_request_treatment treatment;  	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) { -		kfree(reg_request); +	switch (reg_request->initiator) { +	case NL80211_REGDOM_SET_BY_CORE: +		reg_process_hint_core(reg_request);  		return; -	} - -	switch (__regulatory_hint(wiphy, reg_request)) { -	case REG_REQ_ALREADY_SET: -		/* This is required so that the orig_* parameters are saved */ -		if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) -			wiphy_update_regulatory(wiphy, reg_initiator); +	case NL80211_REGDOM_SET_BY_USER: +		treatment = reg_process_hint_user(reg_request); +		if (treatment == REG_REQ_IGNORE || +		    treatment == REG_REQ_ALREADY_SET || +		    treatment == REG_REQ_USER_HINT_HANDLED) +			return; +		queue_delayed_work(system_power_efficient_wq, +				   ®_timeout, msecs_to_jiffies(3142)); +		return; +	case NL80211_REGDOM_SET_BY_DRIVER: +		if (!wiphy) +			goto out_free; +		treatment = reg_process_hint_driver(wiphy, reg_request);  		break; -	default: -		if (reg_initiator == NL80211_REGDOM_SET_BY_USER) -			schedule_delayed_work(®_timeout, -					      msecs_to_jiffies(3142)); +	case NL80211_REGDOM_SET_BY_COUNTRY_IE: +		if (!wiphy) +			goto out_free; +		treatment = reg_process_hint_country_ie(wiphy, reg_request);  		break; +	default: +		WARN(1, "invalid initiator %d\n", reg_request->initiator); +		goto out_free;  	} + +	/* This is required so that the orig_* parameters are saved */ +	if (treatment == REG_REQ_ALREADY_SET && wiphy && +	    wiphy->regulatory_flags & REGULATORY_STRICT_REG) +		wiphy_update_regulatory(wiphy, reg_request->initiator); + +	return; + +out_free: +	reg_free_request(reg_request);  }  /* @@ -1555,7 +1913,7 @@ static void reg_process_pending_hints(void)  	/* When last_request->processed becomes true this will be rescheduled */  	if (lr && !lr->processed) { -		REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); +		reg_process_hint(lr);  		return;  	} @@ -1573,7 +1931,7 @@ static void reg_process_pending_hints(void)  	spin_unlock(®_requests_lock); -	reg_process_hint(reg_request, reg_request->initiator); +	reg_process_hint(reg_request);  }  /* Processes beacon hints -- this has nothing to do with country IEs */ @@ -1665,6 +2023,22 @@ int regulatory_hint_user(const char *alpha2,  	return 0;  } +int regulatory_hint_indoor_user(void) +{ +	struct regulatory_request *request; + +	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); +	if (!request) +		return -ENOMEM; + +	request->wiphy_idx = WIPHY_IDX_INVALID; +	request->initiator = NL80211_REGDOM_SET_BY_USER; +	request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR; +	queue_regulatory_request(request); + +	return 0; +} +  /* Driver hints */  int regulatory_hint(struct wiphy *wiphy, const char *alpha2)  { @@ -1673,6 +2047,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)  	if (WARN_ON(!alpha2 || !wiphy))  		return -EINVAL; +	wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG; +  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);  	if (!request)  		return -ENOMEM; @@ -1689,8 +2065,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)  }  EXPORT_SYMBOL(regulatory_hint); -void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, -			 const u8 *country_ie, u8 country_ie_len) +void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band, +				const u8 *country_ie, u8 country_ie_len)  {  	char alpha2[2];  	enum environment_cap env = ENVIRON_ANY; @@ -1830,6 +2206,8 @@ static void restore_regulatory_settings(bool reset_user)  	ASSERT_RTNL(); +	reg_is_indoor = false; +  	reset_regdomains(true, &world_regdom);  	restore_alpha2(alpha2, reset_user); @@ -1865,7 +2243,7 @@ static void restore_regulatory_settings(bool reset_user)  	world_alpha2[1] = cfg80211_world_regdom->alpha2[1];  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) { -		if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) +		if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)  			restore_custom_reg_settings(&rdev->wiphy);  	} @@ -1965,35 +2343,53 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)  	const struct ieee80211_reg_rule *reg_rule = NULL;  	const struct ieee80211_freq_range *freq_range = NULL;  	const struct ieee80211_power_rule *power_rule = NULL; +	char bw[32], cac_time[32]; -	pr_info("  (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); +	pr_info("  (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)\n");  	for (i = 0; i < rd->n_reg_rules; i++) {  		reg_rule = &rd->reg_rules[i];  		freq_range = ®_rule->freq_range;  		power_rule = ®_rule->power_rule; +		if (reg_rule->flags & NL80211_RRF_AUTO_BW) +			snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO", +				 freq_range->max_bandwidth_khz, +				 reg_get_max_bandwidth(rd, reg_rule)); +		else +			snprintf(bw, sizeof(bw), "%d KHz", +				 freq_range->max_bandwidth_khz); + +		if (reg_rule->flags & NL80211_RRF_DFS) +			scnprintf(cac_time, sizeof(cac_time), "%u s", +				  reg_rule->dfs_cac_ms/1000); +		else +			scnprintf(cac_time, sizeof(cac_time), "N/A"); + +  		/*  		 * There may not be documentation for max antenna gain  		 * in certain regions  		 */  		if (power_rule->max_antenna_gain) -			pr_info("  (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", +			pr_info("  (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)\n",  				freq_range->start_freq_khz,  				freq_range->end_freq_khz, -				freq_range->max_bandwidth_khz, +				bw,  				power_rule->max_antenna_gain, -				power_rule->max_eirp); +				power_rule->max_eirp, +				cac_time);  		else -			pr_info("  (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", +			pr_info("  (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)\n",  				freq_range->start_freq_khz,  				freq_range->end_freq_khz, -				freq_range->max_bandwidth_khz, -				power_rule->max_eirp); +				bw, +				power_rule->max_eirp, +				cac_time);  	}  } -bool reg_supported_dfs_region(u8 dfs_region) +bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)  {  	switch (dfs_region) {  	case NL80211_DFS_UNSET: @@ -2008,27 +2404,6 @@ bool reg_supported_dfs_region(u8 dfs_region)  	}  } -static void print_dfs_region(u8 dfs_region) -{ -	if (!dfs_region) -		return; - -	switch (dfs_region) { -	case NL80211_DFS_FCC: -		pr_info(" DFS Master region FCC"); -		break; -	case NL80211_DFS_ETSI: -		pr_info(" DFS Master region ETSI"); -		break; -	case NL80211_DFS_JP: -		pr_info(" DFS Master region JP"); -		break; -	default: -		pr_info(" DFS Master region Unknown"); -		break; -	} -} -  static void print_regdomain(const struct ieee80211_regdomain *rd)  {  	struct regulatory_request *lr = get_last_request(); @@ -2060,7 +2435,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)  		}  	} -	print_dfs_region(rd->dfs_region); +	pr_info(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));  	print_rd_rules(rd);  } @@ -2070,48 +2445,57 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)  	print_rd_rules(rd);  } -/* Takes ownership of rd only if it doesn't fail */ -static int __set_regdom(const struct ieee80211_regdomain *rd) +static int reg_set_rd_core(const struct ieee80211_regdomain *rd) +{ +	if (!is_world_regdom(rd->alpha2)) +		return -EINVAL; +	update_world_regdomain(rd); +	return 0; +} + +static int reg_set_rd_user(const struct ieee80211_regdomain *rd, +			   struct regulatory_request *user_request)  { -	const struct ieee80211_regdomain *regd;  	const struct ieee80211_regdomain *intersected_rd = NULL; -	struct wiphy *request_wiphy; -	struct regulatory_request *lr = get_last_request(); -	/* Some basic sanity checks first */ +	if (!regdom_changes(rd->alpha2)) +		return -EALREADY; -	if (!reg_is_valid_request(rd->alpha2)) +	if (!is_valid_rd(rd)) { +		pr_err("Invalid regulatory domain detected:\n"); +		print_regdomain_info(rd);  		return -EINVAL; +	} -	if (is_world_regdom(rd->alpha2)) { -		update_world_regdomain(rd); +	if (!user_request->intersect) { +		reset_regdomains(false, rd);  		return 0;  	} -	if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && -	    !is_unknown_alpha2(rd->alpha2)) +	intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); +	if (!intersected_rd)  		return -EINVAL; -	/* -	 * Lets only bother proceeding on the same alpha2 if the current -	 * rd is non static (it means CRDA was present and was used last) -	 * and the pending request came in from a country IE -	 */ -	if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { -		/* -		 * If someone else asked us to change the rd lets only bother -		 * checking if the alpha2 changes if CRDA was already called -		 */ -		if (!regdom_changes(rd->alpha2)) -			return -EALREADY; -	} +	kfree(rd); +	rd = NULL; +	reset_regdomains(false, intersected_rd); -	/* -	 * Now lets set the regulatory domain, update all driver channels -	 * and finally inform them of what we have done, in case they want -	 * to review or adjust their own settings based on their own -	 * internal EEPROM data -	 */ +	return 0; +} + +static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, +			     struct regulatory_request *driver_request) +{ +	const struct ieee80211_regdomain *regd; +	const struct ieee80211_regdomain *intersected_rd = NULL; +	const struct ieee80211_regdomain *tmp; +	struct wiphy *request_wiphy; + +	if (is_world_regdom(rd->alpha2)) +		return -EINVAL; + +	if (!regdom_changes(rd->alpha2)) +		return -EALREADY;  	if (!is_valid_rd(rd)) {  		pr_err("Invalid regulatory domain detected:\n"); @@ -2119,29 +2503,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  		return -EINVAL;  	} -	request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); -	if (!request_wiphy && -	    (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || -	     lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { -		schedule_delayed_work(®_timeout, 0); +	request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx); +	if (!request_wiphy) { +		queue_delayed_work(system_power_efficient_wq, +				   ®_timeout, 0);  		return -ENODEV;  	} -	if (!lr->intersect) { -		if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { -			reset_regdomains(false, rd); -			return 0; -		} - -		/* -		 * For a driver hint, lets copy the regulatory domain the -		 * driver wanted to the wiphy to deal with conflicts -		 */ - -		/* -		 * Userspace could have sent two replies with only -		 * one kernel request. -		 */ +	if (!driver_request->intersect) {  		if (request_wiphy->regd)  			return -EALREADY; @@ -2154,38 +2523,60 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  		return 0;  	} -	/* Intersection requires a bit more work */ +	intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); +	if (!intersected_rd) +		return -EINVAL; -	if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { -		intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); -		if (!intersected_rd) -			return -EINVAL; +	/* +	 * We can trash what CRDA provided now. +	 * However if a driver requested this specific regulatory +	 * domain we keep it for its private use +	 */ +	tmp = get_wiphy_regdom(request_wiphy); +	rcu_assign_pointer(request_wiphy->regd, rd); +	rcu_free_regdom(tmp); -		/* -		 * We can trash what CRDA provided now. -		 * However if a driver requested this specific regulatory -		 * domain we keep it for its private use -		 */ -		if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) { -			const struct ieee80211_regdomain *tmp; +	rd = NULL; -			tmp = get_wiphy_regdom(request_wiphy); -			rcu_assign_pointer(request_wiphy->regd, rd); -			rcu_free_regdom(tmp); -		} else { -			kfree(rd); -		} +	reset_regdomains(false, intersected_rd); -		rd = NULL; +	return 0; +} -		reset_regdomains(false, intersected_rd); +static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd, +				 struct regulatory_request *country_ie_request) +{ +	struct wiphy *request_wiphy; -		return 0; +	if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && +	    !is_unknown_alpha2(rd->alpha2)) +		return -EINVAL; + +	/* +	 * Lets only bother proceeding on the same alpha2 if the current +	 * rd is non static (it means CRDA was present and was used last) +	 * and the pending request came in from a country IE +	 */ + +	if (!is_valid_rd(rd)) { +		pr_err("Invalid regulatory domain detected:\n"); +		print_regdomain_info(rd); +		return -EINVAL;  	} -	return -EINVAL; -} +	request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx); +	if (!request_wiphy) { +		queue_delayed_work(system_power_efficient_wq, +				   ®_timeout, 0); +		return -ENODEV; +	} +	if (country_ie_request->intersect) +		return -EINVAL; + +	reset_regdomains(false, rd); +	return 0; +}  /*   * Use this call to set the current regulatory domain. Conflicts with @@ -2195,15 +2586,45 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  int set_regdom(const struct ieee80211_regdomain *rd)  {  	struct regulatory_request *lr; +	bool user_reset = false;  	int r; +	if (!reg_is_valid_request(rd->alpha2)) { +		kfree(rd); +		return -EINVAL; +	} +  	lr = get_last_request();  	/* Note that this doesn't update the wiphys, this is done below */ -	r = __set_regdom(rd); +	switch (lr->initiator) { +	case NL80211_REGDOM_SET_BY_CORE: +		r = reg_set_rd_core(rd); +		break; +	case NL80211_REGDOM_SET_BY_USER: +		r = reg_set_rd_user(rd, lr); +		user_reset = true; +		break; +	case NL80211_REGDOM_SET_BY_DRIVER: +		r = reg_set_rd_driver(rd, lr); +		break; +	case NL80211_REGDOM_SET_BY_COUNTRY_IE: +		r = reg_set_rd_country_ie(rd, lr); +		break; +	default: +		WARN(1, "invalid initiator %d\n", lr->initiator); +		return -EINVAL; +	} +  	if (r) { -		if (r == -EALREADY) +		switch (r) { +		case -EALREADY:  			reg_set_request_processed(); +			break; +		default: +			/* Back to world regulatory in case of errors */ +			restore_regulatory_settings(user_reset); +		}  		kfree(rd);  		return r; @@ -2225,26 +2646,6 @@ int set_regdom(const struct ieee80211_regdomain *rd)  	return 0;  } -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ -	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; -} -  void wiphy_regulatory_register(struct wiphy *wiphy)  {  	struct regulatory_request *lr; @@ -2267,7 +2668,7 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)  		reg_num_devs_support_basehint--;  	rcu_free_regdom(get_wiphy_regdom(wiphy)); -	rcu_assign_pointer(wiphy->regd, NULL); +	RCU_INIT_POINTER(wiphy->regd, NULL);  	if (lr)  		request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); @@ -2287,6 +2688,40 @@ static void reg_timeout_work(struct work_struct *work)  	rtnl_unlock();  } +/* + * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for + * UNII band definitions + */ +int cfg80211_get_unii(int freq) +{ +	/* UNII-1 */ +	if (freq >= 5150 && freq <= 5250) +		return 0; + +	/* UNII-2A */ +	if (freq > 5250 && freq <= 5350) +		return 1; + +	/* UNII-2B */ +	if (freq > 5350 && freq <= 5470) +		return 2; + +	/* UNII-2C */ +	if (freq > 5470 && freq <= 5725) +		return 3; + +	/* UNII-3 */ +	if (freq > 5725 && freq <= 5825) +		return 4; + +	return -EINVAL; +} + +bool regulatory_indoor_allowed(void) +{ +	return reg_is_indoor; +} +  int __init regulatory_init(void)  {  	int err = 0; @@ -2295,8 +2730,6 @@ int __init regulatory_init(void)  	if (IS_ERR(reg_pdev))  		return PTR_ERR(reg_pdev); -	reg_pdev->dev.type = ®_device_type; -  	spin_lock_init(®_requests_lock);  	spin_lock_init(®_pending_beacons_lock); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index af2d5f8a5d8..5e48031ccb9 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -18,13 +18,15 @@  extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; +bool reg_is_valid_request(const char *alpha2);  bool is_world_regdom(const char *alpha2); -bool reg_supported_dfs_region(u8 dfs_region); +bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region); +enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);  int regulatory_hint_user(const char *alpha2,  			 enum nl80211_user_reg_hint_type user_reg_hint_type); +int regulatory_hint_indoor_user(void); -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env);  void wiphy_regulatory_register(struct wiphy *wiphy);  void wiphy_regulatory_deregister(struct wiphy *wiphy); @@ -32,6 +34,8 @@ int __init regulatory_init(void);  void regulatory_exit(void);  int set_regdom(const struct ieee80211_regdomain *rd); +unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, +				   const struct ieee80211_reg_rule *rule);  bool reg_last_request_cell_base(void); @@ -58,7 +62,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,  				 gfp_t gfp);  /** - * regulatory_hint_11d - hints a country IE as a regulatory domain + * regulatory_hint_country_ie - hints a country IE as a regulatory domain   * @wiphy: the wireless device giving the hint (used only for reporting   *	conflicts)   * @band: the band on which the country IE was received on. This determines @@ -78,7 +82,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,   * not observed. For this reason if a triplet is seen with channel   * information for a band the BSS is not present in it will be ignored.   */ -void regulatory_hint_11d(struct wiphy *wiphy, +void regulatory_hint_country_ie(struct wiphy *wiphy,  			 enum ieee80211_band band,  			 const u8 *country_ie,  			 u8 country_ie_len); @@ -101,4 +105,21 @@ void regulatory_hint_11d(struct wiphy *wiphy,   */  void regulatory_hint_disconnect(void); +/** + * cfg80211_get_unii - get the U-NII band for the frequency + * @freq: the frequency for which we want to get the UNII band. + + * Get a value specifying the U-NII band frequency belongs to. + * U-NII bands are defined by the FCC in C.F.R 47 part 15. + * + * Returns -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A, + * 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3. + */ +int cfg80211_get_unii(int freq); + +/** + * regulatory_indoor_allowed - is indoor operation allowed + */ +bool regulatory_indoor_allowed(void); +  #endif  /* __NET_WIRELESS_REG_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index eeb71480f1a..0798c62e608 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -81,10 +81,10 @@ static void bss_free(struct cfg80211_internal_bss *bss)  	kfree(bss);  } -static inline void bss_ref_get(struct cfg80211_registered_device *dev, +static inline void bss_ref_get(struct cfg80211_registered_device *rdev,  			       struct cfg80211_internal_bss *bss)  { -	lockdep_assert_held(&dev->bss_lock); +	lockdep_assert_held(&rdev->bss_lock);  	bss->refcount++;  	if (bss->pub.hidden_beacon_bss) { @@ -95,10 +95,10 @@ static inline void bss_ref_get(struct cfg80211_registered_device *dev,  	}  } -static inline void bss_ref_put(struct cfg80211_registered_device *dev, +static inline void bss_ref_put(struct cfg80211_registered_device *rdev,  			       struct cfg80211_internal_bss *bss)  { -	lockdep_assert_held(&dev->bss_lock); +	lockdep_assert_held(&rdev->bss_lock);  	if (bss->pub.hidden_beacon_bss) {  		struct cfg80211_internal_bss *hbss; @@ -114,10 +114,10 @@ static inline void bss_ref_put(struct cfg80211_registered_device *dev,  		bss_free(bss);  } -static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, +static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,  				  struct cfg80211_internal_bss *bss)  { -	lockdep_assert_held(&dev->bss_lock); +	lockdep_assert_held(&rdev->bss_lock);  	if (!list_empty(&bss->hidden_list)) {  		/* @@ -134,45 +134,52 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,  	}  	list_del_init(&bss->list); -	rb_erase(&bss->rbn, &dev->bss_tree); -	bss_ref_put(dev, bss); +	rb_erase(&bss->rbn, &rdev->bss_tree); +	bss_ref_put(rdev, bss);  	return true;  } -static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, +static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,  				  unsigned long expire_time)  {  	struct cfg80211_internal_bss *bss, *tmp;  	bool expired = false; -	lockdep_assert_held(&dev->bss_lock); +	lockdep_assert_held(&rdev->bss_lock); -	list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { +	list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {  		if (atomic_read(&bss->hold))  			continue;  		if (!time_after(expire_time, bss->ts))  			continue; -		if (__cfg80211_unlink_bss(dev, bss)) +		if (__cfg80211_unlink_bss(rdev, bss))  			expired = true;  	}  	if (expired) -		dev->bss_generation++; +		rdev->bss_generation++;  } -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, +			   bool send_message)  {  	struct cfg80211_scan_request *request;  	struct wireless_dev *wdev; +	struct sk_buff *msg;  #ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu;  #endif  	ASSERT_RTNL(); -	request = rdev->scan_req; +	if (rdev->scan_msg) { +		nl80211_send_scan_result(rdev, rdev->scan_msg); +		rdev->scan_msg = NULL; +		return; +	} +	request = rdev->scan_req;  	if (!request)  		return; @@ -186,18 +193,16 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)  	if (wdev->netdev)  		cfg80211_sme_scan_done(wdev->netdev); -	if (request->aborted) { -		nl80211_send_scan_aborted(rdev, wdev); -	} 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); +	if (!request->aborted && +	    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);  	} +	msg = nl80211_build_scan_msg(rdev, wdev, request->aborted); +  #ifdef CONFIG_CFG80211_WEXT  	if (wdev->netdev && !request->aborted) {  		memset(&wrqu, 0, sizeof(wrqu)); @@ -210,17 +215,12 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)  		dev_put(wdev->netdev);  	rdev->scan_req = NULL; +	kfree(request); -	/* -	 * OK. If this is invoked with "leak" then we can't -	 * free this ... but we've cleaned it up anyway. The -	 * driver failed to call the scan_done callback, so -	 * all bets are off, it might still be trying to use -	 * the scan request or not ... if it accesses the dev -	 * in there (it shouldn't anyway) then it may crash. -	 */ -	if (!leak) -		kfree(request); +	if (!send_message) +		rdev->scan_msg = msg; +	else +		nl80211_send_scan_result(rdev, msg);  }  void __cfg80211_scan_done(struct work_struct *wk) @@ -231,18 +231,18 @@ void __cfg80211_scan_done(struct work_struct *wk)  			    scan_done_wk);  	rtnl_lock(); -	___cfg80211_scan_done(rdev, false); +	___cfg80211_scan_done(rdev, true);  	rtnl_unlock();  }  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); +	WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);  	request->aborted = aborted;  	request->notified = true; -	queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); +	queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);  }  EXPORT_SYMBOL(cfg80211_scan_done); @@ -254,10 +254,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)  	rdev = container_of(wk, struct cfg80211_registered_device,  			    sched_scan_results_wk); -	request = rdev->sched_scan_req; -  	rtnl_lock(); +	request = rdev->sched_scan_req; +  	/* we don't have sched_scan_req anymore if the scan is stopping */  	if (request) {  		if (request->flags & NL80211_SCAN_FLAG_FLUSH) { @@ -278,20 +278,28 @@ 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) +	if (wiphy_to_rdev(wiphy)->sched_scan_req)  		queue_work(cfg80211_wq, -			   &wiphy_to_dev(wiphy)->sched_scan_results_wk); +			   &wiphy_to_rdev(wiphy)->sched_scan_results_wk);  }  EXPORT_SYMBOL(cfg80211_sched_scan_results); -void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + +	ASSERT_RTNL();  	trace_cfg80211_sched_scan_stopped(wiphy); -	rtnl_lock();  	__cfg80211_stop_sched_scan(rdev, true); +} +EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl); + +void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +{ +	rtnl_lock(); +	cfg80211_sched_scan_stopped_rtnl(wiphy);  	rtnl_unlock();  }  EXPORT_SYMBOL(cfg80211_sched_scan_stopped); @@ -322,21 +330,21 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,  	return 0;  } -void cfg80211_bss_age(struct cfg80211_registered_device *dev, +void cfg80211_bss_age(struct cfg80211_registered_device *rdev,                        unsigned long age_secs)  {  	struct cfg80211_internal_bss *bss;  	unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); -	spin_lock_bh(&dev->bss_lock); -	list_for_each_entry(bss, &dev->bss_list, list) +	spin_lock_bh(&rdev->bss_lock); +	list_for_each_entry(bss, &rdev->bss_list, list)  		bss->ts -= age_jiffies; -	spin_unlock_bh(&dev->bss_lock); +	spin_unlock_bh(&rdev->bss_lock);  } -void cfg80211_bss_expire(struct cfg80211_registered_device *dev) +void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)  { -	__cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE); +	__cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);  }  const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) @@ -526,32 +534,34 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,  				      const u8 *ssid, size_t ssid_len,  				      u16 capa_mask, u16 capa_val)  { -	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(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); +	spin_lock_bh(&rdev->bss_lock); -	list_for_each_entry(bss, &dev->bss_list, list) { +	list_for_each_entry(bss, &rdev->bss_list, list) {  		if ((bss->pub.capability & capa_mask) != capa_val)  			continue;  		if (channel && bss->pub.channel != channel)  			continue; +		if (!is_valid_ether_addr(bss->pub.bssid)) +			continue;  		/* Don't get expired BSS structs */  		if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&  		    !atomic_read(&bss->hold))  			continue;  		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {  			res = bss; -			bss_ref_get(dev, res); +			bss_ref_get(rdev, res);  			break;  		}  	} -	spin_unlock_bh(&dev->bss_lock); +	spin_unlock_bh(&rdev->bss_lock);  	if (!res)  		return NULL;  	trace_cfg80211_return_bss(&res->pub); @@ -559,10 +569,10 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,  }  EXPORT_SYMBOL(cfg80211_get_bss); -static void rb_insert_bss(struct cfg80211_registered_device *dev, +static void rb_insert_bss(struct cfg80211_registered_device *rdev,  			  struct cfg80211_internal_bss *bss)  { -	struct rb_node **p = &dev->bss_tree.rb_node; +	struct rb_node **p = &rdev->bss_tree.rb_node;  	struct rb_node *parent = NULL;  	struct cfg80211_internal_bss *tbss;  	int cmp; @@ -585,15 +595,15 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,  	}  	rb_link_node(&bss->rbn, parent, p); -	rb_insert_color(&bss->rbn, &dev->bss_tree); +	rb_insert_color(&bss->rbn, &rdev->bss_tree);  }  static struct cfg80211_internal_bss * -rb_find_bss(struct cfg80211_registered_device *dev, +rb_find_bss(struct cfg80211_registered_device *rdev,  	    struct cfg80211_internal_bss *res,  	    enum bss_compare_mode mode)  { -	struct rb_node *n = dev->bss_tree.rb_node; +	struct rb_node *n = rdev->bss_tree.rb_node;  	struct cfg80211_internal_bss *bss;  	int r; @@ -612,7 +622,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,  	return NULL;  } -static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, +static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,  				   struct cfg80211_internal_bss *new)  {  	const struct cfg80211_bss_ies *ies; @@ -642,7 +652,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,  	/* This is the bad part ... */ -	list_for_each_entry(bss, &dev->bss_list, list) { +	list_for_each_entry(bss, &rdev->bss_list, list) {  		if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))  			continue;  		if (bss->pub.channel != new->pub.channel) @@ -659,9 +669,6 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,  			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))) @@ -679,8 +686,9 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,  /* Returned bss is reference counted and must be cleaned up appropriately. */  static struct cfg80211_internal_bss * -cfg80211_bss_update(struct cfg80211_registered_device *dev, -		    struct cfg80211_internal_bss *tmp) +cfg80211_bss_update(struct cfg80211_registered_device *rdev, +		    struct cfg80211_internal_bss *tmp, +		    bool signal_valid)  {  	struct cfg80211_internal_bss *found = NULL; @@ -689,14 +697,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,  	tmp->ts = jiffies; -	spin_lock_bh(&dev->bss_lock); +	spin_lock_bh(&rdev->bss_lock);  	if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) { -		spin_unlock_bh(&dev->bss_lock); +		spin_unlock_bh(&rdev->bss_lock);  		return NULL;  	} -	found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR); +	found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);  	if (found) {  		/* Update IEs */ @@ -765,7 +773,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,  		}  		found->pub.beacon_interval = tmp->pub.beacon_interval; -		found->pub.signal = tmp->pub.signal; +		/* +		 * don't update the signal if beacon was heard on +		 * adjacent channel. +		 */ +		if (signal_valid) +			found->pub.signal = tmp->pub.signal;  		found->pub.capability = tmp->pub.capability;  		found->ts = tmp->ts;  	} else { @@ -778,7 +791,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,  		 * 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, +		new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,  			      GFP_ATOMIC);  		if (!new) {  			ies = (void *)rcu_dereference(tmp->pub.beacon_ies); @@ -794,9 +807,9 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,  		INIT_LIST_HEAD(&new->hidden_list);  		if (rcu_access_pointer(tmp->pub.proberesp_ies)) { -			hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); +			hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);  			if (!hidden) -				hidden = rb_find_bss(dev, tmp, +				hidden = rb_find_bss(rdev, tmp,  						     BSS_CMP_HIDE_NUL);  			if (hidden) {  				new->pub.hidden_beacon_bss = &hidden->pub; @@ -813,24 +826,24 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,  			 * expensive search for any probe responses that should  			 * be grouped with this beacon for updates ...  			 */ -			if (!cfg80211_combine_bsses(dev, new)) { +			if (!cfg80211_combine_bsses(rdev, new)) {  				kfree(new);  				goto drop;  			}  		} -		list_add_tail(&new->list, &dev->bss_list); -		rb_insert_bss(dev, new); +		list_add_tail(&new->list, &rdev->bss_list); +		rb_insert_bss(rdev, new);  		found = new;  	} -	dev->bss_generation++; -	bss_ref_get(dev, found); -	spin_unlock_bh(&dev->bss_lock); +	rdev->bss_generation++; +	bss_ref_get(rdev, found); +	spin_unlock_bh(&rdev->bss_lock);  	return found;   drop: -	spin_unlock_bh(&dev->bss_lock); +	spin_unlock_bh(&rdev->bss_lock);  	return NULL;  } @@ -869,14 +882,16 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,  /* Returned bss is reference counted and must be cleaned up appropriately. */  struct cfg80211_bss*  cfg80211_inform_bss_width(struct wiphy *wiphy, -			  struct ieee80211_channel *channel, +			  struct ieee80211_channel *rx_channel,  			  enum nl80211_bss_scan_width scan_width,  			  const u8 *bssid, u64 tsf, u16 capability,  			  u16 beacon_interval, const u8 *ie, size_t ielen,  			  s32 signal, gfp_t gfp)  {  	struct cfg80211_bss_ies *ies; +	struct ieee80211_channel *channel;  	struct cfg80211_internal_bss tmp = {}, *res; +	bool signal_valid;  	if (WARN_ON(!wiphy))  		return NULL; @@ -885,7 +900,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,  			(signal < 0 || signal > 100)))  		return NULL; -	channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel); +	channel = cfg80211_get_bss_channel(wiphy, ie, ielen, rx_channel);  	if (!channel)  		return NULL; @@ -913,7 +928,9 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,  	rcu_assign_pointer(tmp.pub.beacon_ies, ies);  	rcu_assign_pointer(tmp.pub.ies, ies); -	res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp); +	signal_valid = abs(rx_channel->center_freq - channel->center_freq) <= +		wiphy->max_adj_channel_rssi_comp; +	res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);  	if (!res)  		return NULL; @@ -929,20 +946,22 @@ EXPORT_SYMBOL(cfg80211_inform_bss_width);  /* Returned bss is reference counted and must be cleaned up appropriately. */  struct cfg80211_bss *  cfg80211_inform_bss_width_frame(struct wiphy *wiphy, -				struct ieee80211_channel *channel, +				struct ieee80211_channel *rx_channel,  				enum nl80211_bss_scan_width scan_width,  				struct ieee80211_mgmt *mgmt, size_t len,  				s32 signal, gfp_t gfp)  {  	struct cfg80211_internal_bss tmp = {}, *res;  	struct cfg80211_bss_ies *ies; +	struct ieee80211_channel *channel; +	bool signal_valid;  	size_t ielen = len - offsetof(struct ieee80211_mgmt,  				      u.probe_resp.variable);  	BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=  			offsetof(struct ieee80211_mgmt, u.beacon.variable)); -	trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt, +	trace_cfg80211_inform_bss_width_frame(wiphy, rx_channel, scan_width, mgmt,  					      len, signal);  	if (WARN_ON(!mgmt)) @@ -959,7 +978,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,  		return NULL;  	channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, -					   ielen, channel); +					   ielen, rx_channel);  	if (!channel)  		return NULL; @@ -983,7 +1002,9 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,  	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), &tmp); +	signal_valid = abs(rx_channel->center_freq - channel->center_freq) <= +		wiphy->max_adj_channel_rssi_comp; +	res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);  	if (!res)  		return NULL; @@ -998,7 +1019,7 @@ EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);  void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  { -	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct cfg80211_internal_bss *bss;  	if (!pub) @@ -1006,15 +1027,15 @@ void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  	bss = container_of(pub, struct cfg80211_internal_bss, pub); -	spin_lock_bh(&dev->bss_lock); -	bss_ref_get(dev, bss); -	spin_unlock_bh(&dev->bss_lock); +	spin_lock_bh(&rdev->bss_lock); +	bss_ref_get(rdev, bss); +	spin_unlock_bh(&rdev->bss_lock);  }  EXPORT_SYMBOL(cfg80211_ref_bss);  void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  { -	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct cfg80211_internal_bss *bss;  	if (!pub) @@ -1022,15 +1043,15 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  	bss = container_of(pub, struct cfg80211_internal_bss, pub); -	spin_lock_bh(&dev->bss_lock); -	bss_ref_put(dev, bss); -	spin_unlock_bh(&dev->bss_lock); +	spin_lock_bh(&rdev->bss_lock); +	bss_ref_put(rdev, bss); +	spin_unlock_bh(&rdev->bss_lock);  }  EXPORT_SYMBOL(cfg80211_put_bss);  void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  { -	struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct cfg80211_internal_bss *bss;  	if (WARN_ON(!pub)) @@ -1038,12 +1059,12 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)  	bss = container_of(pub, struct cfg80211_internal_bss, pub); -	spin_lock_bh(&dev->bss_lock); +	spin_lock_bh(&rdev->bss_lock);  	if (!list_empty(&bss->list)) { -		if (__cfg80211_unlink_bss(dev, bss)) -			dev->bss_generation++; +		if (__cfg80211_unlink_bss(rdev, bss)) +			rdev->bss_generation++;  	} -	spin_unlock_bh(&dev->bss_lock); +	spin_unlock_bh(&rdev->bss_lock);  }  EXPORT_SYMBOL(cfg80211_unlink_bss); @@ -1060,7 +1081,7 @@ cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)  	if (!dev)  		return ERR_PTR(-ENODEV);  	if (dev->ieee80211_ptr) -		rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); +		rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);  	else  		rdev = ERR_PTR(-ENODEV);  	dev_put(dev); @@ -1089,7 +1110,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,  	if (IS_ERR(rdev))  		return PTR_ERR(rdev); -	if (rdev->scan_req) { +	if (rdev->scan_req || rdev->scan_msg) {  		err = -EBUSY;  		goto out;  	} @@ -1099,11 +1120,8 @@ int cfg80211_wext_siwscan(struct net_device *dev,  	/* Determine number of channels, needed to allocate creq */  	if (wreq && wreq->num_channels)  		n_channels = wreq->num_channels; -	else { -		for (band = 0; band < IEEE80211_NUM_BANDS; band++) -			if (wiphy->bands[band]) -				n_channels += wiphy->bands[band]->n_channels; -	} +	else +		n_channels = ieee80211_get_num_supported_channels(wiphy);  	creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +  		       n_channels * sizeof(void *), @@ -1143,7 +1161,11 @@ int cfg80211_wext_siwscan(struct net_device *dev,  				int k;  				int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;  				for (k = 0; k < wreq->num_channels; k++) { -					int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]); +					struct iw_freq *freq = +						&wreq->channel_list[k]; +					int wext_freq = +						cfg80211_wext_freq(freq); +  					if (wext_freq == wiphy_freq)  						goto wext_freq_found;  				} @@ -1455,7 +1477,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,  } -static int ieee80211_scan_results(struct cfg80211_registered_device *dev, +static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,  				  struct iw_request_info *info,  				  char *buf, size_t len)  { @@ -1463,18 +1485,18 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *dev,  	char *end_buf = buf + len;  	struct cfg80211_internal_bss *bss; -	spin_lock_bh(&dev->bss_lock); -	cfg80211_bss_expire(dev); +	spin_lock_bh(&rdev->bss_lock); +	cfg80211_bss_expire(rdev); -	list_for_each_entry(bss, &dev->bss_list, list) { +	list_for_each_entry(bss, &rdev->bss_list, list) {  		if (buf + len - current_ev <= IW_EV_ADDR_LEN) { -			spin_unlock_bh(&dev->bss_lock); +			spin_unlock_bh(&rdev->bss_lock);  			return -E2BIG;  		} -		current_ev = ieee80211_bss(&dev->wiphy, info, bss, +		current_ev = ieee80211_bss(&rdev->wiphy, info, bss,  					   current_ev, end_buf);  	} -	spin_unlock_bh(&dev->bss_lock); +	spin_unlock_bh(&rdev->bss_lock);  	return current_ev - buf;  } @@ -1494,7 +1516,7 @@ int cfg80211_wext_giwscan(struct net_device *dev,  	if (IS_ERR(rdev))  		return PTR_ERR(rdev); -	if (rdev->scan_req) +	if (rdev->scan_req || rdev->scan_msg)  		return -EAGAIN;  	res = ieee80211_scan_results(rdev, info, extra, data->length); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 20e86a95dc4..8bbeeb30221 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -59,29 +59,21 @@ static void cfg80211_sme_free(struct wireless_dev *wdev)  static int cfg80211_conn_scan(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_scan_request *request;  	int n_channels, err;  	ASSERT_RTNL(); -	ASSERT_RDEV_LOCK(rdev);  	ASSERT_WDEV_LOCK(wdev); -	if (rdev->scan_req) +	if (rdev->scan_req || rdev->scan_msg)  		return -EBUSY; -	if (wdev->conn->params.channel) { +	if (wdev->conn->params.channel)  		n_channels = 1; -	} else { -		enum ieee80211_band band; -		n_channels = 0; +	else +		n_channels = ieee80211_get_num_supported_channels(wdev->wiphy); -		for (band = 0; band < IEEE80211_NUM_BANDS; band++) { -			if (!wdev->wiphy->bands[band]) -				continue; -			n_channels += wdev->wiphy->bands[band]->n_channels; -		} -	}  	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +  			  sizeof(request->channels[0]) * n_channels,  			  GFP_KERNEL); @@ -138,7 +130,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)  static int cfg80211_conn_do_work(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_connect_params *params;  	struct cfg80211_assoc_request req = {};  	int err; @@ -157,7 +149,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)  	case CFG80211_CONN_SCAN_AGAIN:  		return cfg80211_conn_scan(wdev);  	case CFG80211_CONN_AUTHENTICATE_NEXT: -		BUG_ON(!rdev->ops->auth); +		if (WARN_ON(!rdev->ops->auth)) +			return -EOPNOTSUPP;  		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;  		return cfg80211_mlme_auth(rdev, wdev->netdev,  					  params->channel, params->auth_type, @@ -169,7 +162,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)  	case CFG80211_CONN_AUTH_FAILED:  		return -ENOTCONN;  	case CFG80211_CONN_ASSOCIATE_NEXT: -		BUG_ON(!rdev->ops->assoc); +		if (WARN_ON(!rdev->ops->assoc)) +			return -EOPNOTSUPP;  		wdev->conn->state = CFG80211_CONN_ASSOCIATING;  		if (wdev->conn->prev_bssid_valid)  			req.prev_bssid = wdev->conn->prev_bssid; @@ -242,7 +236,6 @@ void cfg80211_conn_work(struct work_struct *work)  					NULL, 0, NULL, 0,  					WLAN_STATUS_UNSPECIFIED_FAILURE,  					false, NULL); -			cfg80211_sme_free(wdev);  		}  		wdev_unlock(wdev);  	} @@ -253,7 +246,7 @@ void cfg80211_conn_work(struct work_struct *work)  /* Returned bss is reference counted and must be cleaned up appropriately. */  static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_bss *bss;  	u16 capa = WLAN_CAPABILITY_ESS; @@ -283,7 +276,7 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)  static void __cfg80211_sme_scan_done(struct net_device *dev)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_bss *bss;  	ASSERT_WDEV_LOCK(wdev); @@ -314,7 +307,7 @@ void cfg80211_sme_scan_done(struct net_device *dev)  void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)  {  	struct wiphy *wiphy = wdev->wiphy; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;  	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); @@ -360,7 +353,7 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)  bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (!wdev->conn)  		return false; @@ -394,7 +387,7 @@ void cfg80211_sme_deauth(struct wireless_dev *wdev)  void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (!wdev->conn)  		return; @@ -405,7 +398,7 @@ void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)  void cfg80211_sme_disassoc(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (!wdev->conn)  		return; @@ -416,7 +409,7 @@ void cfg80211_sme_disassoc(struct wireless_dev *wdev)  void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	if (!wdev->conn)  		return; @@ -429,7 +422,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,  				struct cfg80211_connect_params *connect,  				const u8 *prev_bssid)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_bss *bss;  	int err; @@ -476,7 +469,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,  	}  	wdev->conn->params.ssid = wdev->ssid; -	wdev->conn->params.ssid_len = connect->ssid_len; +	wdev->conn->params.ssid_len = wdev->ssid_len;  	/* see if we have the bss already */  	bss = cfg80211_get_conn_bss(wdev); @@ -488,7 +481,6 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,  	/* we're good if we have a matching bss struct */  	if (bss) { -		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;  		err = cfg80211_conn_do_work(wdev);  		cfg80211_put_bss(wdev->wiphy, bss);  	} else { @@ -514,7 +506,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,  static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	int err;  	if (!wdev->conn) @@ -602,7 +594,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  		return;  	} -	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, +	nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,  				    bssid, req_ie, req_ie_len,  				    resp_ie, resp_ie_len,  				    status, GFP_KERNEL); @@ -632,6 +624,16 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  	}  #endif +	if (!bss && (status == WLAN_STATUS_SUCCESS)) { +		WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); +		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, +				       wdev->ssid, wdev->ssid_len, +				       WLAN_CAPABILITY_ESS, +				       WLAN_CAPABILITY_ESS); +		if (bss) +			cfg80211_hold_bss(bss_from_pub(bss)); +	} +  	if (wdev->current_bss) {  		cfg80211_unhold_bss(wdev->current_bss);  		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); @@ -646,19 +648,12 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  			cfg80211_unhold_bss(bss_from_pub(bss));  			cfg80211_put_bss(wdev->wiphy, bss);  		} +		cfg80211_sme_free(wdev);  		return;  	} -	if (!bss) { -		WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect); -		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, -				       wdev->ssid, wdev->ssid_len, -				       WLAN_CAPABILITY_ESS, -				       WLAN_CAPABILITY_ESS); -		if (WARN_ON(!bss)) -			return; -		cfg80211_hold_bss(bss_from_pub(bss)); -	} +	if (WARN_ON(!bss)) +		return;  	wdev->current_bss = bss_from_pub(bss); @@ -682,8 +677,8 @@ 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_country_ie(wdev->wiphy, bss->channel->band, +				   country_ie + 2, country_ie[1]);  	kfree(country_ie);  } @@ -693,7 +688,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,  			     u16 status, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_event *ev;  	unsigned long flags; @@ -748,7 +743,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev,  	cfg80211_hold_bss(bss_from_pub(bss));  	wdev->current_bss = bss_from_pub(bss); -	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid, +	nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy), +			    wdev->netdev, bss->bssid,  			    req_ie, req_ie_len, resp_ie, resp_ie_len,  			    GFP_KERNEL); @@ -807,7 +803,7 @@ void cfg80211_roamed_bss(struct net_device *dev,  			 size_t resp_ie_len, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_event *ev;  	unsigned long flags; @@ -840,7 +836,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,  			     size_t ie_len, u16 reason, bool from_ap)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	int i;  #ifdef CONFIG_CFG80211_WEXT  	union iwreq_data wrqu; @@ -870,6 +866,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,  		for (i = 0; i < 6; i++)  			rdev_del_key(rdev, dev, i, false, NULL); +	rdev_set_qos_map(rdev, dev, NULL); +  #ifdef CONFIG_CFG80211_WEXT  	memset(&wrqu, 0, sizeof(wrqu));  	wrqu.ap_addr.sa_family = ARPHRD_ETHER; @@ -881,10 +879,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,  }  void cfg80211_disconnected(struct net_device *dev, u16 reason, -			   u8 *ie, size_t ie_len, gfp_t gfp) +			   const u8 *ie, size_t ie_len, gfp_t gfp)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_event *ev;  	unsigned long flags; diff --git a/net/wireless/sysfs.h b/net/wireless/sysfs.h index 65acbebd371..b533ed71daf 100644 --- a/net/wireless/sysfs.h +++ b/net/wireless/sysfs.h @@ -1,8 +1,8 @@  #ifndef __WIRELESS_SYSFS_H  #define __WIRELESS_SYSFS_H -extern int wiphy_sysfs_init(void); -extern void wiphy_sysfs_exit(void); +int wiphy_sysfs_init(void); +void wiphy_sysfs_exit(void);  extern struct class ieee80211_class; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ba5f0d6614d..7cc887f9da1 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -186,6 +186,28 @@  #define BOOL_TO_STR(bo) (bo) ? "true" : "false" +#define QOS_MAP_ENTRY __field(u8, num_des)			\ +		      __array(u8, dscp_exception,		\ +			      2 * IEEE80211_QOS_MAP_MAX_EX)	\ +		      __array(u8, up, IEEE80211_QOS_MAP_LEN_MIN) +#define QOS_MAP_ASSIGN(qos_map)					\ +	do {							\ +		if ((qos_map)) {				\ +			__entry->num_des = (qos_map)->num_des;	\ +			memcpy(__entry->dscp_exception,		\ +			       &(qos_map)->dscp_exception,	\ +			       2 * IEEE80211_QOS_MAP_MAX_EX);	\ +			memcpy(__entry->up, &(qos_map)->up,	\ +			       IEEE80211_QOS_MAP_LEN_MIN);	\ +		} else {					\ +			__entry->num_des = 0;			\ +			memset(__entry->dscp_exception, 0,	\ +			       2 * IEEE80211_QOS_MAP_MAX_EX);	\ +			memset(__entry->up, 0,			\ +			       IEEE80211_QOS_MAP_LEN_MIN);	\ +		}						\ +	} while (0) +  /*************************************************************   *			rdev->ops traces		     *   *************************************************************/ @@ -1446,9 +1468,10 @@ TRACE_EVENT(rdev_sched_scan_start,  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), +		 u16 status_code, u32 peer_capability, +		 const u8 *buf, size_t len),  	TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code, -		buf, len), +		peer_capability, buf, len),  	TP_STRUCT__entry(  		WIPHY_ENTRY  		NETDEV_ENTRY @@ -1456,6 +1479,7 @@ TRACE_EVENT(rdev_tdls_mgmt,  		__field(u8, action_code)  		__field(u8, dialog_token)  		__field(u16, status_code) +		__field(u32, peer_capability)  		__dynamic_array(u8, buf, len)  	),  	TP_fast_assign( @@ -1465,13 +1489,15 @@ TRACE_EVENT(rdev_tdls_mgmt,  		__entry->action_code = action_code;  		__entry->dialog_token = dialog_token;  		__entry->status_code = status_code; +		__entry->peer_capability = peer_capability;  		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 ", +		  "dialog_token: %u, status_code: %u, peer_capability: %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]) +		  __entry->status_code, __entry->peer_capability, +		  ((u8 *)__get_dynamic_array(buf))[0])  );  TRACE_EVENT(rdev_dump_survey, @@ -1653,9 +1679,8 @@ TRACE_EVENT(rdev_cancel_remain_on_channel,  TRACE_EVENT(rdev_mgmt_tx,  	TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, -		 struct ieee80211_channel *chan, bool offchan, -		 unsigned int wait, bool no_cck, bool dont_wait_for_ack), -	TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack), +		 struct cfg80211_mgmt_tx_params *params), +	TP_ARGS(wiphy, wdev, params),  	TP_STRUCT__entry(  		WIPHY_ENTRY  		WDEV_ENTRY @@ -1668,11 +1693,11 @@ TRACE_EVENT(rdev_mgmt_tx,  	TP_fast_assign(  		WIPHY_ASSIGN;  		WDEV_ASSIGN; -		CHAN_ASSIGN(chan); -		__entry->offchan = offchan; -		__entry->wait = wait; -		__entry->no_cck = no_cck; -		__entry->dont_wait_for_ack = dont_wait_for_ack; +		CHAN_ASSIGN(params->chan); +		__entry->offchan = params->offchan; +		__entry->wait = params->wait; +		__entry->no_cck = params->no_cck; +		__entry->dont_wait_for_ack = params->dont_wait_for_ack;  	),  	TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s,"  		  " wait: %u, no cck: %s, dont wait for ack: %s", @@ -1851,29 +1876,69 @@ TRACE_EVENT(rdev_channel_switch,  		WIPHY_ENTRY  		NETDEV_ENTRY  		CHAN_DEF_ENTRY -		__field(u16, counter_offset_beacon) -		__field(u16, counter_offset_presp)  		__field(bool, radar_required)  		__field(bool, block_tx)  		__field(u8, count) +		__dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon) +		__dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp)  	),  	TP_fast_assign(  		WIPHY_ASSIGN;  		NETDEV_ASSIGN;  		CHAN_DEF_ASSIGN(¶ms->chandef); -		__entry->counter_offset_beacon = params->counter_offset_beacon; -		__entry->counter_offset_presp = params->counter_offset_presp;  		__entry->radar_required = params->radar_required;  		__entry->block_tx = params->block_tx;  		__entry->count = params->count; +		memcpy(__get_dynamic_array(bcn_ofs), +		       params->counter_offsets_beacon, +		       params->n_counter_offsets_beacon * sizeof(u16)); + +		/* probe response offsets are optional */ +		if (params->n_counter_offsets_presp) +			memcpy(__get_dynamic_array(pres_ofs), +			       params->counter_offsets_presp, +			       params->n_counter_offsets_presp * sizeof(u16));  	),  	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT -		  ", block_tx: %d, count: %u, radar_required: %d" -		  ", counter offsets (beacon/presp): %u/%u", +		  ", block_tx: %d, count: %u, radar_required: %d",  		  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG, -		  __entry->block_tx, __entry->count, __entry->radar_required, -		  __entry->counter_offset_beacon, -		  __entry->counter_offset_presp) +		  __entry->block_tx, __entry->count, __entry->radar_required) +); + +TRACE_EVENT(rdev_set_qos_map, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_qos_map *qos_map), +	TP_ARGS(wiphy, netdev, qos_map), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		QOS_MAP_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		QOS_MAP_ASSIGN(qos_map); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", num_des: %u", +		  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des) +); + +TRACE_EVENT(rdev_set_ap_chanwidth, +	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, +		 struct cfg80211_chan_def *chandef), +	TP_ARGS(wiphy, netdev, chandef), +	TP_STRUCT__entry( +		WIPHY_ENTRY +		NETDEV_ENTRY +		CHAN_DEF_ENTRY +	), +	TP_fast_assign( +		WIPHY_ASSIGN; +		NETDEV_ASSIGN; +		CHAN_DEF_ASSIGN(chandef); +	), +	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT, +		  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)  );  /************************************************************* @@ -2029,7 +2094,8 @@ TRACE_EVENT(cfg80211_michael_mic_failure,  		MAC_ASSIGN(addr, addr);  		__entry->key_type = key_type;  		__entry->key_id = key_id; -		memcpy(__entry->tsc, tsc, 6); +		if (tsc) +			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, @@ -2150,18 +2216,21 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify,  );  TRACE_EVENT(cfg80211_reg_can_beacon, -	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), -	TP_ARGS(wiphy, chandef), +	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, +		 enum nl80211_iftype iftype), +	TP_ARGS(wiphy, chandef, iftype),  	TP_STRUCT__entry(  		WIPHY_ENTRY  		CHAN_DEF_ENTRY +		__field(enum nl80211_iftype, iftype)  	),  	TP_fast_assign(  		WIPHY_ASSIGN;  		CHAN_DEF_ASSIGN(chandef); +		__entry->iftype = iftype;  	), -	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, -		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d", +		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype)  );  TRACE_EVENT(cfg80211_chandef_dfs_required, @@ -2239,11 +2308,6 @@ DECLARE_EVENT_CLASS(cfg80211_rx_evt,  	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) @@ -2254,6 +2318,24 @@ DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame,  	TP_ARGS(netdev, addr)  ); +TRACE_EVENT(cfg80211_ibss_joined, +	TP_PROTO(struct net_device *netdev, const u8 *bssid, +		 struct ieee80211_channel *channel), +	TP_ARGS(netdev, bssid, channel), +	TP_STRUCT__entry( +		NETDEV_ENTRY +		MAC_ENTRY(bssid) +		CHAN_ENTRY +	), +	TP_fast_assign( +		NETDEV_ASSIGN; +		MAC_ASSIGN(bssid, bssid); +		CHAN_ASSIGN(channel); +	), +	TP_printk(NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", " CHAN_PR_FMT, +		  NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG) +); +  TRACE_EVENT(cfg80211_probe_status,  	TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie,  		 bool acked), @@ -2559,6 +2641,21 @@ TRACE_EVENT(cfg80211_ft_event,  		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))  ); +TRACE_EVENT(cfg80211_stop_iface, +	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) +); +  #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */  #undef TRACE_INCLUDE_PATH diff --git a/net/wireless/util.c b/net/wireless/util.c index ce090c1c5e4..728f1c0dc70 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -10,6 +10,8 @@  #include <net/cfg80211.h>  #include <net/ip.h>  #include <net/dsfield.h> +#include <linux/if_vlan.h> +#include <linux/mpls.h>  #include "core.h"  #include "rdev-ops.h" @@ -474,7 +476,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,  EXPORT_SYMBOL(ieee80211_data_to_8023);  int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, -			     enum nl80211_iftype iftype, u8 *bssid, bool qos) +			     enum nl80211_iftype iftype, +			     const u8 *bssid, bool qos)  {  	struct ieee80211_hdr hdr;  	u16 hdrlen, ethertype; @@ -688,9 +691,11 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,  EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);  /* Given a data frame determine the 802.1p/1d tag to use. */ -unsigned int cfg80211_classify8021d(struct sk_buff *skb) +unsigned int cfg80211_classify8021d(struct sk_buff *skb, +				    struct cfg80211_qos_map *qos_map)  {  	unsigned int dscp; +	unsigned char vlan_priority;  	/* skb->priority values from 256->263 are magic values to  	 * directly indicate a specific 802.1d priority.  This is used @@ -700,6 +705,13 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb)  	if (skb->priority >= 256 && skb->priority <= 263)  		return skb->priority - 256; +	if (vlan_tx_tag_present(skb)) { +		vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK) +			>> VLAN_PRIO_SHIFT; +		if (vlan_priority > 0) +			return vlan_priority; +	} +  	switch (skb->protocol) {  	case htons(ETH_P_IP):  		dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc; @@ -707,10 +719,40 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb)  	case htons(ETH_P_IPV6):  		dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc;  		break; +	case htons(ETH_P_MPLS_UC): +	case htons(ETH_P_MPLS_MC): { +		struct mpls_label mpls_tmp, *mpls; + +		mpls = skb_header_pointer(skb, sizeof(struct ethhdr), +					  sizeof(*mpls), &mpls_tmp); +		if (!mpls) +			return 0; + +		return (ntohl(mpls->entry) & MPLS_LS_TC_MASK) +			>> MPLS_LS_TC_SHIFT; +	} +	case htons(ETH_P_80221): +		/* 802.21 is always network control traffic */ +		return 7;  	default:  		return 0;  	} +	if (qos_map) { +		unsigned int i, tmp_dscp = dscp >> 2; + +		for (i = 0; i < qos_map->num_des; i++) { +			if (tmp_dscp == qos_map->dscp_exception[i].dscp) +				return qos_map->dscp_exception[i].up; +		} + +		for (i = 0; i < 8; i++) { +			if (tmp_dscp >= qos_map->up[i].low && +			    tmp_dscp <= qos_map->up[i].high) +				return i; +		} +	} +  	return dscp >> 5;  }  EXPORT_SYMBOL(cfg80211_classify8021d); @@ -729,7 +771,7 @@ EXPORT_SYMBOL(ieee80211_bss_get_ie);  void cfg80211_upload_connect_keys(struct wireless_dev *wdev)  { -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct net_device *dev = wdev->netdev;  	int i; @@ -795,7 +837,11 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)  						ev->dc.reason, true);  			break;  		case EVENT_IBSS_JOINED: -			__cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); +			__cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid, +					       ev->ij.channel); +			break; +		case EVENT_STOPPED: +			__cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev);  			break;  		}  		wdev_unlock(wdev); @@ -812,7 +858,6 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)  	struct wireless_dev *wdev;  	ASSERT_RTNL(); -	ASSERT_RDEV_LOCK(rdev);  	list_for_each_entry(wdev, &rdev->wdev_list, list)  		cfg80211_process_wdev_events(wdev); @@ -825,7 +870,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,  	int err;  	enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; -	ASSERT_RDEV_LOCK(rdev); +	ASSERT_RTNL();  	/* don't support changing VLANs, you just re-create them */  	if (otype == NL80211_IFTYPE_AP_VLAN) @@ -847,17 +892,15 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,  		return -EBUSY;  	if (ntype != otype && netif_running(dev)) { -		err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, -						    ntype); -		if (err) -			return err; -  		dev->ieee80211_ptr->use_4addr = false;  		dev->ieee80211_ptr->mesh_id_up_len = 0; +		wdev_lock(dev->ieee80211_ptr); +		rdev_set_qos_map(rdev, dev, NULL); +		wdev_unlock(dev->ieee80211_ptr);  		switch (otype) {  		case NL80211_IFTYPE_AP: -			cfg80211_stop_ap(rdev, dev); +			cfg80211_stop_ap(rdev, dev, true);  			break;  		case NL80211_IFTYPE_ADHOC:  			cfg80211_leave_ibss(rdev, dev, false); @@ -1224,6 +1267,120 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,  	return res;  } +int cfg80211_iter_combinations(struct wiphy *wiphy, +			       const int num_different_channels, +			       const u8 radar_detect, +			       const int iftype_num[NUM_NL80211_IFTYPES], +			       void (*iter)(const struct ieee80211_iface_combination *c, +					    void *data), +			       void *data) +{ +	const struct ieee80211_regdomain *regdom; +	enum nl80211_dfs_regions region = 0; +	int i, j, iftype; +	int num_interfaces = 0; +	u32 used_iftypes = 0; + +	if (radar_detect) { +		rcu_read_lock(); +		regdom = rcu_dereference(cfg80211_regdomain); +		if (regdom) +			region = regdom->dfs_region; +		rcu_read_unlock(); +	} + +	for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { +		num_interfaces += iftype_num[iftype]; +		if (iftype_num[iftype] > 0 && +		    !(wiphy->software_iftypes & BIT(iftype))) +			used_iftypes |= BIT(iftype); +	} + +	for (i = 0; i < wiphy->n_iface_combinations; i++) { +		const struct ieee80211_iface_combination *c; +		struct ieee80211_iface_limit *limits; +		u32 all_iftypes = 0; + +		c = &wiphy->iface_combinations[i]; + +		if (num_interfaces > c->max_interfaces) +			continue; +		if (num_different_channels > c->num_different_channels) +			continue; + +		limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, +				 GFP_KERNEL); +		if (!limits) +			return -ENOMEM; + +		for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { +			if (wiphy->software_iftypes & BIT(iftype)) +				continue; +			for (j = 0; j < c->n_limits; j++) { +				all_iftypes |= limits[j].types; +				if (!(limits[j].types & BIT(iftype))) +					continue; +				if (limits[j].max < iftype_num[iftype]) +					goto cont; +				limits[j].max -= iftype_num[iftype]; +			} +		} + +		if (radar_detect != (c->radar_detect_widths & radar_detect)) +			goto cont; + +		if (radar_detect && c->radar_detect_regions && +		    !(c->radar_detect_regions & BIT(region))) +			goto cont; + +		/* Finally check that all iftypes that we're currently +		 * using are actually part of this combination. If they +		 * aren't then we can't use this combination and have +		 * to continue to the next. +		 */ +		if ((all_iftypes & used_iftypes) != used_iftypes) +			goto cont; + +		/* This combination covered all interface types and +		 * supported the requested numbers, so we're good. +		 */ + +		(*iter)(c, data); + cont: +		kfree(limits); +	} + +	return 0; +} +EXPORT_SYMBOL(cfg80211_iter_combinations); + +static void +cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c, +			  void *data) +{ +	int *num = data; +	(*num)++; +} + +int cfg80211_check_combinations(struct wiphy *wiphy, +				const int num_different_channels, +				const u8 radar_detect, +				const int iftype_num[NUM_NL80211_IFTYPES]) +{ +	int err, num = 0; + +	err = cfg80211_iter_combinations(wiphy, num_different_channels, +					 radar_detect, iftype_num, +					 cfg80211_iter_sum_ifcombs, &num); +	if (err) +		return err; +	if (num == 0) +		return -EBUSY; + +	return 0; +} +EXPORT_SYMBOL(cfg80211_check_combinations); +  int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  				 struct wireless_dev *wdev,  				 enum nl80211_iftype iftype, @@ -1232,7 +1389,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  				 u8 radar_detect)  {  	struct wireless_dev *wdev_iter; -	u32 used_iftypes = BIT(iftype);  	int num[NUM_NL80211_IFTYPES];  	struct ieee80211_channel  			*used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS]; @@ -1240,37 +1396,14 @@ 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; +	int i;  	ASSERT_RTNL();  	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) +	if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))  		return -EINVAL;  	/* Always allow software iftypes */ @@ -1285,6 +1418,11 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  	num[iftype] = 1; +	/* TODO: We'll probably not need this anymore, since this +	 * should only be called with CHAN_MODE_UNDEFINED. There are +	 * still a couple of pending calls where other chanmodes are +	 * used, but we should get rid of them. +	 */  	switch (chanmode) {  	case CHAN_MODE_UNDEFINED:  		break; @@ -1322,7 +1460,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  		 */  		mutex_lock_nested(&wdev_iter->mtx, 1);  		__acquire(wdev_iter->mtx); -		cfg80211_get_chan_state(wdev_iter, &ch, &chmode); +		cfg80211_get_chan_state(wdev_iter, &ch, &chmode, &radar_detect);  		wdev_unlock(wdev_iter);  		switch (chmode) { @@ -1348,65 +1486,13 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,  		num[wdev_iter->iftype]++;  		total++; -		used_iftypes |= BIT(wdev_iter->iftype);  	}  	if (total == 1 && !radar_detect)  		return 0; -	for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { -		const struct ieee80211_iface_combination *c; -		struct ieee80211_iface_limit *limits; -		u32 all_iftypes = 0; - -		c = &rdev->wiphy.iface_combinations[i]; - -		if (total > c->max_interfaces) -			continue; -		if (num_different_channels > c->num_different_channels) -			continue; - -		limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, -				 GFP_KERNEL); -		if (!limits) -			return -ENOMEM; - -		for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { -			if (rdev->wiphy.software_iftypes & BIT(iftype)) -				continue; -			for (j = 0; j < c->n_limits; j++) { -				all_iftypes |= limits[j].types; -				if (!(limits[j].types & BIT(iftype))) -					continue; -				if (limits[j].max < num[iftype]) -					goto cont; -				limits[j].max -= num[iftype]; -			} -		} - -		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 -		 * aren't then we can't use this combination and have -		 * to continue to the next. -		 */ -		if ((all_iftypes & used_iftypes) != used_iftypes) -			goto cont; - -		/* -		 * This combination covered all interface types and -		 * supported the requested numbers, so we're good. -		 */ -		kfree(limits); -		return 0; - cont: -		kfree(limits); -	} - -	return -EBUSY; +	return cfg80211_check_combinations(&rdev->wiphy, num_different_channels, +					   radar_detect, num);  }  int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, @@ -1447,6 +1533,37 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,  	return 0;  } +unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy) +{ +	enum ieee80211_band band; +	unsigned int n_channels = 0; + +	for (band = 0; band < IEEE80211_NUM_BANDS; band++) +		if (wiphy->bands[band]) +			n_channels += wiphy->bands[band]->n_channels; + +	return n_channels; +} +EXPORT_SYMBOL(ieee80211_get_num_supported_channels); + +int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, +			 struct station_info *sinfo) +{ +	struct cfg80211_registered_device *rdev; +	struct wireless_dev *wdev; + +	wdev = dev->ieee80211_ptr; +	if (!wdev) +		return -EOPNOTSUPP; + +	rdev = wiphy_to_rdev(wdev->wiphy); +	if (!rdev->ops->get_station) +		return -EOPNOTSUPP; + +	return rdev_get_station(rdev, dev, mac_addr, sinfo); +} +EXPORT_SYMBOL(cfg80211_get_station); +  /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */  /* Ethernet-II snap header (RFC1042 for most EtherTypes) */  const unsigned char rfc1042_header[] __aligned(2) = diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index e7c6e862580..11120bb1416 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -73,7 +73,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,  	struct vif_params vifparams;  	enum nl80211_iftype type; -	rdev = wiphy_to_dev(wdev->wiphy); +	rdev = wiphy_to_rdev(wdev->wiphy);  	switch (*mode) {  	case IW_MODE_INFRA: @@ -253,12 +253,12 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);  /**   * cfg80211_wext_freq - get wext frequency for non-"auto" - * @wiphy: the wiphy + * @dev: the net device   * @freq: the wext freq encoding   *   * Returns a frequency, or a negative error code, or 0 for auto.   */ -int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) +int cfg80211_wext_freq(struct iw_freq *freq)  {  	/*  	 * Parse frequency - return 0 for auto and @@ -286,7 +286,7 @@ int cfg80211_wext_siwrts(struct net_device *dev,  			 struct iw_param *rts, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	u32 orts = wdev->wiphy->rts_threshold;  	int err; @@ -324,7 +324,7 @@ int cfg80211_wext_siwfrag(struct net_device *dev,  			  struct iw_param *frag, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	u32 ofrag = wdev->wiphy->frag_threshold;  	int err; @@ -364,13 +364,13 @@ static int cfg80211_wext_siwretry(struct net_device *dev,  				  struct iw_param *retry, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	u32 changed = 0;  	u8 olong = wdev->wiphy->retry_long;  	u8 oshort = wdev->wiphy->retry_short;  	int err; -	if (retry->disabled || +	if (retry->disabled || retry->value < 1 || retry->value > 255 ||  	    (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)  		return -EINVAL; @@ -412,9 +412,9 @@ int cfg80211_wext_giwretry(struct net_device *dev,  		 * First return short value, iwconfig will ask long value  		 * later if needed  		 */ -		retry->flags |= IW_RETRY_LIMIT; +		retry->flags |= IW_RETRY_LIMIT | IW_RETRY_SHORT;  		retry->value = wdev->wiphy->retry_short; -		if (wdev->wiphy->retry_long != wdev->wiphy->retry_short) +		if (wdev->wiphy->retry_long == wdev->wiphy->retry_short)  			retry->flags |= IW_RETRY_LONG;  		return 0; @@ -587,7 +587,7 @@ static int cfg80211_wext_siwencode(struct net_device *dev,  				   struct iw_point *erq, char *keybuf)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	int idx, err;  	bool remove = false;  	struct key_params params; @@ -647,7 +647,7 @@ static int cfg80211_wext_siwencodeext(struct net_device *dev,  				      struct iw_point *erq, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;  	const u8 *addr;  	int idx; @@ -775,7 +775,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,  				 struct iw_freq *wextfreq, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_chan_def chandef = {  		.width = NL80211_CHAN_WIDTH_20_NOHT,  	}; @@ -787,7 +787,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,  	case NL80211_IFTYPE_ADHOC:  		return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);  	case NL80211_IFTYPE_MONITOR: -		freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); +		freq = cfg80211_wext_freq(wextfreq);  		if (freq < 0)  			return freq;  		if (freq == 0) @@ -798,7 +798,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,  			return -EINVAL;  		return cfg80211_set_monitor_channel(rdev, &chandef);  	case NL80211_IFTYPE_MESH_POINT: -		freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); +		freq = cfg80211_wext_freq(wextfreq);  		if (freq < 0)  			return freq;  		if (freq == 0) @@ -818,7 +818,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,  				 struct iw_freq *freq, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_chan_def chandef;  	int ret; @@ -847,7 +847,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,  				    union iwreq_data *data, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	enum nl80211_tx_power_setting type;  	int dbm = 0; @@ -899,7 +899,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,  				    union iwreq_data *data, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	int err, val;  	if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) @@ -1119,7 +1119,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,  				  struct iw_param *wrq, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	bool ps = wdev->ps;  	int timeout = wdev->ps_timeout;  	int err; @@ -1177,7 +1177,7 @@ static int cfg80211_wds_wext_siwap(struct net_device *dev,  				   struct sockaddr *addr, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	int err;  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) @@ -1221,7 +1221,7 @@ static int cfg80211_wext_siwrate(struct net_device *dev,  				 struct iw_param *rate, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_bitrate_mask mask;  	u32 fixed, maxrate;  	struct ieee80211_supported_band *sband; @@ -1272,7 +1272,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,  				 struct iw_param *rate, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	/* we are under RTNL - globally locked - so can use a static struct */  	static struct station_info sinfo;  	u8 addr[ETH_ALEN]; @@ -1310,7 +1310,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,  static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	/* we are under RTNL - globally locked - so can use static structs */  	static struct iw_statistics wstats;  	static struct station_info sinfo; @@ -1449,7 +1449,7 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev,  				  struct iw_point *data, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct cfg80211_pmksa cfg_pmksa;  	struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; diff --git a/net/wireless/wext-compat.h b/net/wireless/wext-compat.h index 5d766b0118e..ebcacca2f73 100644 --- a/net/wireless/wext-compat.h +++ b/net/wireless/wext-compat.h @@ -50,7 +50,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,  			   struct iw_point *data, char *extra); -int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); +int cfg80211_wext_freq(struct iw_freq *freq);  extern const struct iw_handler_def cfg80211_wext_handler; diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 14c9a2583ba..c7e5c8eb4f2 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -21,7 +21,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,  	const u8 *prev_bssid = NULL;  	int err, i; -	ASSERT_RDEV_LOCK(rdev); +	ASSERT_RTNL();  	ASSERT_WDEV_LOCK(wdev);  	if (!netif_running(wdev->netdev)) @@ -67,7 +67,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,  			      struct iw_freq *wextfreq, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	struct ieee80211_channel *chan = NULL;  	int err, freq; @@ -75,7 +75,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))  		return -EINVAL; -	freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); +	freq = cfg80211_wext_freq(wextfreq);  	if (freq < 0)  		return freq; @@ -169,7 +169,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,  			       struct iw_point *data, char *ssid)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	size_t len = data->length;  	int err; @@ -260,7 +260,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,  			    struct sockaddr *ap_addr, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	u8 *bssid = ap_addr->sa_data;  	int err; @@ -333,7 +333,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,  			   struct iw_point *data, char *extra)  {  	struct wireless_dev *wdev = dev->ieee80211_ptr; -	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);  	u8 *ie = extra;  	int ie_len = data->length, err; @@ -390,7 +390,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev,  	if (!wdev)  		return -EOPNOTSUPP; -	rdev = wiphy_to_dev(wdev->wiphy); +	rdev = wiphy_to_rdev(wdev->wiphy);  	if (wdev->iftype != NL80211_IFTYPE_STATION)  		return -EINVAL;  | 
