diff options
Diffstat (limited to 'net/wireless/chan.c')
| -rw-r--r-- | net/wireless/chan.c | 455 | 
1 files changed, 404 insertions, 51 deletions
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;  }  | 
