diff options
Diffstat (limited to 'net/wireless/reg.c')
| -rw-r--r-- | net/wireless/reg.c | 158 | 
1 files changed, 139 insertions, 19 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 15f347477a99..2ded3c7fad06 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -97,9 +97,16 @@ const struct ieee80211_regdomain *cfg80211_regdomain;   *     - cfg80211_world_regdom   *     - cfg80211_regdom   *     - last_request + *     - reg_num_devs_support_basehint   */  static DEFINE_MUTEX(reg_mutex); +/* + * Number of devices that registered to the core + * that support cellular base station regulatory hints + */ +static int reg_num_devs_support_basehint; +  static inline void assert_reg_lock(void)  {  	lockdep_assert_held(®_mutex); @@ -129,7 +136,7 @@ static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work);  /* We keep a static world regulatory domain in case of the absence of CRDA */  static const struct ieee80211_regdomain world_regdom = { -	.n_reg_rules = 5, +	.n_reg_rules = 6,  	.alpha2 =  "00",  	.reg_rules = {  		/* IEEE 802.11b/g, channels 1..11 */ @@ -156,6 +163,9 @@ static const struct ieee80211_regdomain world_regdom = {  		REG_RULE(5745-10, 5825+10, 40, 6, 20,  			NL80211_RRF_PASSIVE_SCAN |  			NL80211_RRF_NO_IBSS), + +		/* IEEE 802.11ad (60gHz), channels 1..3 */ +		REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),  	}  }; @@ -670,6 +680,8 @@ static u32 map_regdom_flags(u32 rd_flags)  		channel_flags |= IEEE80211_CHAN_NO_IBSS;  	if (rd_flags & NL80211_RRF_DFS)  		channel_flags |= IEEE80211_CHAN_RADAR; +	if (rd_flags & NL80211_RRF_NO_OFDM) +		channel_flags |= IEEE80211_CHAN_NO_OFDM;  	return channel_flags;  } @@ -891,7 +903,21 @@ static void handle_channel(struct wiphy *wiphy,  	chan->max_antenna_gain = min(chan->orig_mag,  		(int) MBI_TO_DBI(power_rule->max_antenna_gain));  	chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); -	chan->max_power = min(chan->max_power, chan->max_reg_power); +	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. +		 */ +		if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && +		    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && +		    wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) +			chan->max_power = chan->max_reg_power; +		else +			chan->max_power = min(chan->orig_mpwr, +					      chan->max_reg_power); +	} else +		chan->max_power = chan->max_reg_power;  }  static void handle_band(struct wiphy *wiphy, @@ -908,6 +934,61 @@ static void handle_band(struct wiphy *wiphy,  		handle_channel(wiphy, initiator, band, i);  } +static bool reg_request_cell_base(struct regulatory_request *request) +{ +	if (request->initiator != NL80211_REGDOM_SET_BY_USER) +		return false; +	if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) +		return false; +	return true; +} + +bool reg_last_request_cell_base(void) +{ +	bool val; +	assert_cfg80211_lock(); + +	mutex_lock(®_mutex); +	val = reg_request_cell_base(last_request); +	mutex_unlock(®_mutex); +	return val; +} + +#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS + +/* Core specific check */ +static int reg_ignore_cell_hint(struct regulatory_request *pending_request) +{ +	if (!reg_num_devs_support_basehint) +		return -EOPNOTSUPP; + +	if (reg_request_cell_base(last_request)) { +		if (!regdom_changes(pending_request->alpha2)) +			return -EALREADY; +		return 0; +	} +	return 0; +} + +/* Device specific check */ +static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) +{ +	if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) +		return true; +	return false; +} +#else +static int reg_ignore_cell_hint(struct regulatory_request *pending_request) +{ +	return -EOPNOTSUPP; +} +static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) +{ +	return true; +} +#endif + +  static bool ignore_reg_update(struct wiphy *wiphy,  			      enum nl80211_reg_initiator initiator)  { @@ -941,6 +1022,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,  		return true;  	} +	if (reg_request_cell_base(last_request)) +		return reg_dev_ignore_cell_hint(wiphy); +  	return false;  } @@ -1166,14 +1250,6 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,  		wiphy->reg_notifier(wiphy, last_request);  } -void regulatory_update(struct wiphy *wiphy, -		       enum nl80211_reg_initiator setby) -{ -	mutex_lock(®_mutex); -	wiphy_update_regulatory(wiphy, setby); -	mutex_unlock(®_mutex); -} -  static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)  {  	struct cfg80211_registered_device *rdev; @@ -1304,6 +1380,13 @@ static int ignore_request(struct wiphy *wiphy,  		return 0;  	case NL80211_REGDOM_SET_BY_COUNTRY_IE: +		if (reg_request_cell_base(last_request)) { +			/* Trust a Cell base station over the AP's country IE */ +			if (regdom_changes(pending_request->alpha2)) +				return -EOPNOTSUPP; +			return -EALREADY; +		} +  		last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);  		if (unlikely(!is_an_alpha2(pending_request->alpha2))) @@ -1348,6 +1431,12 @@ static int ignore_request(struct wiphy *wiphy,  		return REG_INTERSECT;  	case NL80211_REGDOM_SET_BY_USER: +		if (reg_request_cell_base(pending_request)) +			return reg_ignore_cell_hint(pending_request); + +		if (reg_request_cell_base(last_request)) +			return -EOPNOTSUPP; +  		if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)  			return REG_INTERSECT;  		/* @@ -1389,7 +1478,7 @@ static void reg_set_request_processed(void)  	spin_unlock(®_requests_lock);  	if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) -		cancel_delayed_work_sync(®_timeout); +		cancel_delayed_work(®_timeout);  	if (need_more_processing)  		schedule_work(®_work); @@ -1637,7 +1726,8 @@ static int regulatory_hint_core(const char *alpha2)  }  /* User hints */ -int regulatory_hint_user(const char *alpha2) +int regulatory_hint_user(const char *alpha2, +			 enum nl80211_user_reg_hint_type user_reg_hint_type)  {  	struct regulatory_request *request; @@ -1651,6 +1741,7 @@ int regulatory_hint_user(const char *alpha2)  	request->alpha2[0] = alpha2[0];  	request->alpha2[1] = alpha2[1];  	request->initiator = NL80211_REGDOM_SET_BY_USER; +	request->user_reg_hint_type = user_reg_hint_type;  	queue_regulatory_request(request); @@ -1810,6 +1901,7 @@ static void restore_custom_reg_settings(struct wiphy *wiphy)  			chan->flags = chan->orig_flags;  			chan->max_antenna_gain = chan->orig_mag;  			chan->max_power = chan->orig_mpwr; +			chan->beacon_found = false;  		}  	}  } @@ -1903,7 +1995,7 @@ static void restore_regulatory_settings(bool reset_user)  	 * settings, user regulatory settings takes precedence.  	 */  	if (is_an_alpha2(alpha2)) -		regulatory_hint_user(user_alpha2); +		regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER);  	if (list_empty(&tmp_reg_req_list))  		return; @@ -2078,9 +2170,16 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)  	else {  		if (is_unknown_alpha2(rd->alpha2))  			pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); -		else -			pr_info("Regulatory domain changed to country: %c%c\n", -				rd->alpha2[0], rd->alpha2[1]); +		else { +			if (reg_request_cell_base(last_request)) +				pr_info("Regulatory domain changed " +					"to country: %c%c by Cell Station\n", +					rd->alpha2[0], rd->alpha2[1]); +			else +				pr_info("Regulatory domain changed " +					"to country: %c%c\n", +					rd->alpha2[0], rd->alpha2[1]); +		}  	}  	print_dfs_region(rd->dfs_region);  	print_rd_rules(rd); @@ -2125,7 +2224,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)  		 * checking if the alpha2 changes if CRDA was already called  		 */  		if (!regdom_changes(rd->alpha2)) -			return -EINVAL; +			return -EALREADY;  	}  	/* @@ -2245,6 +2344,9 @@ int set_regdom(const struct ieee80211_regdomain *rd)  	/* Note that this doesn't update the wiphys, this is done below */  	r = __set_regdom(rd);  	if (r) { +		if (r == -EALREADY) +			reg_set_request_processed(); +  		kfree(rd);  		mutex_unlock(®_mutex);  		return r; @@ -2287,8 +2389,22 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)  }  #endif /* CONFIG_HOTPLUG */ +void wiphy_regulatory_register(struct wiphy *wiphy) +{ +	assert_cfg80211_lock(); + +	mutex_lock(®_mutex); + +	if (!reg_dev_ignore_cell_hint(wiphy)) +		reg_num_devs_support_basehint++; + +	wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); + +	mutex_unlock(®_mutex); +} +  /* Caller must hold cfg80211_mutex */ -void reg_device_remove(struct wiphy *wiphy) +void wiphy_regulatory_deregister(struct wiphy *wiphy)  {  	struct wiphy *request_wiphy = NULL; @@ -2296,6 +2412,9 @@ void reg_device_remove(struct wiphy *wiphy)  	mutex_lock(®_mutex); +	if (!reg_dev_ignore_cell_hint(wiphy)) +		reg_num_devs_support_basehint--; +  	kfree(wiphy->regd);  	if (last_request) @@ -2361,7 +2480,8 @@ int __init regulatory_init(void)  	 * as a user hint.  	 */  	if (!is_world_regdom(ieee80211_regdom)) -		regulatory_hint_user(ieee80211_regdom); +		regulatory_hint_user(ieee80211_regdom, +				     NL80211_USER_REG_HINT_USER);  	return 0;  }  | 
