4389 lines · 113.4 KB · C
RawDownload
// SPDX-License-Identifier: ISC/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2007	Johannes Berg <johannes@sipsolutions.net> * Copyright 2008-2011	Luis R. Rodriguez <mcgrof@qca.qualcomm.com> * Copyright 2013-2014  Intel Mobile Communications GmbH * Copyright      2017  Intel Deutschland GmbH * Copyright (C) 2018 - 2026 Intel Corporation */  /** * DOC: Wireless regulatory infrastructure * * The usual implementation is for a driver to read a device EEPROM to * determine which regulatory domain it should be operating under, then * looking up the allowable channels in a driver-local table and finally * registering those channels in the wiphy structure. * * Another set of compliance enforcement is for drivers to use their * own compliance limits which can be stored on the EEPROM. The host * driver or firmware may ensure these are used. * * In addition to all this we provide an extra layer of regulatory * conformance. For drivers which do not have any regulatory * information CRDA provides the complete regulatory solution. * For others it provides a community effort on further restrictions * to enhance compliance. * * Note: When number of rules --> infinity we will not be able to * index on alpha2 any more, instead we'll probably have to * rely on some SHA1 checksum of the regdomain for example. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/export.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/ctype.h>#include <linux/nl80211.h>#include <linux/device/faux.h>#include <linux/verification.h>#include <linux/moduleparam.h>#include <linux/firmware.h>#include <linux/units.h> #include <net/cfg80211.h>#include "core.h"#include "reg.h"#include "rdev-ops.h"#include "nl80211.h" /* * Grace period we give before making sure all current interfaces reside on * channels allowed by the current regulatory domain. */#define REG_ENFORCE_GRACE_MS 60000 /** * 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. */enum reg_request_treatment {	REG_REQ_OK,	REG_REQ_IGNORE,	REG_REQ_INTERSECT,	REG_REQ_ALREADY_SET,}; static struct regulatory_request core_request_world = {	.initiator = NL80211_REGDOM_SET_BY_CORE,	.alpha2[0] = '0',	.alpha2[1] = '0',	.intersect = false,	.processed = true,	.country_ie_env = ENVIRON_ANY,}; /* * Receipt of information from last regulatory request, * protected by RTNL (and can be accessed with RCU protection) */static struct regulatory_request __rcu *last_request =	(void __force __rcu *)&core_request_world; /* To trigger userspace events and load firmware */static struct faux_device *reg_fdev; /* * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no * information to give us an alpha2. * (protected by RTNL, can be read under RCU) */const struct ieee80211_regdomain __rcu *cfg80211_regdomain; /* * Number of devices that registered to the core * that support cellular base station regulatory hints * (protected by RTNL) */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. */static bool reg_is_indoor;static DEFINE_SPINLOCK(reg_indoor_lock); /* Used to track the userspace process controlling the indoor setting */static u32 reg_is_indoor_portid; static void restore_regulatory_settings(bool reset_user, bool cached);static void print_regdomain(const struct ieee80211_regdomain *rd);static void reg_process_hint(struct regulatory_request *reg_request); static const struct ieee80211_regdomain *get_cfg80211_regdom(void){	return rcu_dereference_rtnl(cfg80211_regdomain);} /* * Returns the regulatory domain associated with the wiphy. * * Requires any of RTNL, wiphy mutex or RCU protection. */const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy){	return rcu_dereference_check(wiphy->regd,				     lockdep_is_held(&wiphy->mtx) ||				     lockdep_rtnl_is_held());}EXPORT_SYMBOL(get_wiphy_regdom); 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;	enum nl80211_dfs_regions dfs_region; 	rcu_read_lock();	regd = get_cfg80211_regdom();	dfs_region = regd->dfs_region; 	if (!wiphy)		goto out; 	wiphy_regd = get_wiphy_regdom(wiphy);	if (!wiphy_regd)		goto out; 	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {		dfs_region = wiphy_regd->dfs_region;		goto out;	} 	if (wiphy_regd->dfs_region == regd->dfs_region)		goto out; 	pr_debug("%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:	rcu_read_unlock(); 	return dfs_region;} static void rcu_free_regdom(const struct ieee80211_regdomain *r){	if (!r)		return;	kfree_rcu((struct ieee80211_regdomain *)r, rcu_head);} static struct regulatory_request *get_last_request(void){	return rcu_dereference_rtnl(last_request);} /* Used to queue up regulatory hints */static LIST_HEAD(reg_requests_list);static DEFINE_SPINLOCK(reg_requests_lock); /* Used to queue up beacon hints for review */static LIST_HEAD(reg_pending_beacons);static DEFINE_SPINLOCK(reg_pending_beacons_lock); /* Used to keep track of processed beacon hints */static LIST_HEAD(reg_beacon_list); struct reg_beacon {	struct list_head list;	struct ieee80211_channel chan;}; static void reg_check_chans_work(struct work_struct *work);static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work); static void reg_todo(struct work_struct *work);static DECLARE_WORK(reg_work, reg_todo); /* We keep a static world regulatory domain in case of the absence of CRDA */static const struct ieee80211_regdomain world_regdom = {	.n_reg_rules = 8,	.alpha2 =  "00",	.reg_rules = {		/* IEEE 802.11b/g, channels 1..11 */		REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),		/* IEEE 802.11b/g, channels 12..13. */		REG_RULE(2467-10, 2472+10, 20, 6, 20,			NL80211_RRF_NO_IR | NL80211_RRF_AUTO_BW),		/* 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_NO_IR |			NL80211_RRF_NO_OFDM),		/* IEEE 802.11a, channel 36..48 */		REG_RULE(5180-10, 5240+10, 80, 6, 20,                        NL80211_RRF_NO_IR |                        NL80211_RRF_AUTO_BW), 		/* IEEE 802.11a, channel 52..64 - DFS required */		REG_RULE(5260-10, 5320+10, 80, 6, 20,			NL80211_RRF_NO_IR |			NL80211_RRF_AUTO_BW |			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_NO_IR), 		/* IEEE 802.11ad (60GHz), channels 1..3 */		REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),	}}; /* protected by RTNL */static const struct ieee80211_regdomain *cfg80211_world_regdom =	&world_regdom; static char *ieee80211_regdom = "00";static char user_alpha2[2];static const struct ieee80211_regdomain *cfg80211_user_regdom; 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 == &core_request_world)		return; 	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; 	ASSERT_RTNL(); 	r = get_cfg80211_regdom(); 	/* avoid freeing static information or freeing something twice */	if (r == cfg80211_world_regdom)		r = NULL;	if (cfg80211_world_regdom == &world_regdom)		cfg80211_world_regdom = NULL;	if (r == &world_regdom)		r = NULL; 	rcu_free_regdom(r);	rcu_free_regdom(cfg80211_world_regdom); 	cfg80211_world_regdom = &world_regdom;	rcu_assign_pointer(cfg80211_regdomain, new_regdom); 	if (!full_reset)		return; 	reg_update_last_request(&core_request_world);} /* * Dynamic world regulatory domain requested by the wireless * core upon initialization */static void update_world_regdomain(const struct ieee80211_regdomain *rd){	struct regulatory_request *lr; 	lr = get_last_request(); 	WARN_ON(!lr); 	reset_regdomains(false, rd); 	cfg80211_world_regdom = rd;} bool is_world_regdom(const char *alpha2){	if (!alpha2)		return false;	return alpha2[0] == '0' && alpha2[1] == '0';} static bool is_alpha2_set(const char *alpha2){	if (!alpha2)		return false;	return alpha2[0] && alpha2[1];} static bool is_unknown_alpha2(const char *alpha2){	if (!alpha2)		return false;	/*	 * Special case where regulatory domain was built by driver	 * but a specific alpha2 cannot be determined	 */	return alpha2[0] == '9' && alpha2[1] == '9';} static bool is_intersected_alpha2(const char *alpha2){	if (!alpha2)		return false;	/*	 * Special case where regulatory domain is the	 * result of an intersection between two regulatory domain	 * structures	 */	return alpha2[0] == '9' && alpha2[1] == '8';} static bool is_an_alpha2(const char *alpha2){	if (!alpha2)		return false;	return isascii(alpha2[0]) && isalpha(alpha2[0]) &&	       isascii(alpha2[1]) && isalpha(alpha2[1]);} static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y){	if (!alpha2_x || !alpha2_y)		return false;	return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1];} static bool regdom_changes(const char *alpha2){	const struct ieee80211_regdomain *r = get_cfg80211_regdom(); 	if (!r)		return true;	return !alpha2_equal(r->alpha2, alpha2);} /* * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER * has ever been issued. */static bool is_user_regdom_saved(void){	if (user_alpha2[0] == '9' && user_alpha2[1] == '7')		return false; 	/* This would indicate a mistake on the design */	if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2),		 "Unexpected user alpha2: %c%c\n",		 user_alpha2[0], user_alpha2[1]))		return false; 	return true;} static const struct ieee80211_regdomain *reg_copy_regd(const struct ieee80211_regdomain *src_regd){	struct ieee80211_regdomain *regd;	unsigned int i; 	regd = kzalloc_flex(*regd, reg_rules, src_regd->n_reg_rules);	if (!regd)		return ERR_PTR(-ENOMEM); 	memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); 	for (i = 0; i < src_regd->n_reg_rules; i++)		memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],		       sizeof(struct ieee80211_reg_rule)); 	return regd;} static void cfg80211_save_user_regdom(const struct ieee80211_regdomain *rd){	ASSERT_RTNL(); 	if (!IS_ERR(cfg80211_user_regdom))		kfree(cfg80211_user_regdom);	cfg80211_user_regdom = reg_copy_regd(rd);} struct reg_regdb_apply_request {	struct list_head list;	const struct ieee80211_regdomain *regdom;}; static LIST_HEAD(reg_regdb_apply_list);static DEFINE_MUTEX(reg_regdb_apply_mutex); static void reg_regdb_apply(struct work_struct *work){	struct reg_regdb_apply_request *request; 	rtnl_lock(); 	mutex_lock(&reg_regdb_apply_mutex);	while (!list_empty(&reg_regdb_apply_list)) {		request = list_first_entry(&reg_regdb_apply_list,					   struct reg_regdb_apply_request,					   list);		list_del(&request->list); 		set_regdom(request->regdom, REGD_SOURCE_INTERNAL_DB);		kfree(request);	}	mutex_unlock(&reg_regdb_apply_mutex); 	rtnl_unlock();} static DECLARE_WORK(reg_regdb_work, reg_regdb_apply); static int reg_schedule_apply(const struct ieee80211_regdomain *regdom){	struct reg_regdb_apply_request *request; 

Lines 1–500 of 4389.