2416 lines · 62.4 KB · C
RawDownload
/* * 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> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */  /** * 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/random.h>#include <linux/ctype.h>#include <linux/nl80211.h>#include <linux/platform_device.h>#include <linux/moduleparam.h>#include <net/cfg80211.h>#include "core.h"#include "reg.h"#include "regdb.h"#include "nl80211.h" #ifdef CONFIG_CFG80211_REG_DEBUG#define REG_DBG_PRINT(format, args...)			\	printk(KERN_DEBUG pr_fmt(format), ##args)#else#define REG_DBG_PRINT(args...)#endif 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 */static struct regulatory_request *last_request = &core_request_world; /* 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 * information to give us an alpha2 */const struct ieee80211_regdomain *cfg80211_regdomain; /* * Protects static reg.c components: *     - cfg80211_world_regdom *     - cfg80211_regdom *     - last_request */static DEFINE_MUTEX(reg_mutex); static inline void assert_reg_lock(void){	lockdep_assert_held(&reg_mutex);} /* Used to queue up regulatory hints */static LIST_HEAD(reg_requests_list);static spinlock_t reg_requests_lock; /* Used to queue up beacon hints for review */static LIST_HEAD(reg_pending_beacons);static spinlock_t 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_todo(struct work_struct *work);static DECLARE_WORK(reg_work, reg_todo); static void reg_timeout_work(struct work_struct *work);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,	.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. No HT40		 * channel fits here. */		REG_RULE(2467-10, 2472+10, 20, 6, 20,			NL80211_RRF_PASSIVE_SCAN |			NL80211_RRF_NO_IBSS),		/* 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_OFDM),		/* IEEE 802.11a, channel 36..48 */		REG_RULE(5180-10, 5240+10, 40, 6, 20,                        NL80211_RRF_PASSIVE_SCAN |                        NL80211_RRF_NO_IBSS), 		/* NB: 5260 MHz - 5700 MHz requies DFS */ 		/* IEEE 802.11a, channel 149..165 */		REG_RULE(5745-10, 5825+10, 40, 6, 20,			NL80211_RRF_PASSIVE_SCAN |			NL80211_RRF_NO_IBSS),	}}; static const struct ieee80211_regdomain *cfg80211_world_regdom =	&world_regdom; static char *ieee80211_regdom = "00";static char user_alpha2[2]; module_param(ieee80211_regdom, charp, 0444);MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); static void reset_regdomains(bool full_reset){	/* avoid freeing static information or freeing something twice */	if (cfg80211_regdomain == cfg80211_world_regdom)		cfg80211_regdomain = NULL;	if (cfg80211_world_regdom == &world_regdom)		cfg80211_world_regdom = NULL;	if (cfg80211_regdomain == &world_regdom)		cfg80211_regdomain = NULL; 	kfree(cfg80211_regdomain);	kfree(cfg80211_world_regdom); 	cfg80211_world_regdom = &world_regdom;	cfg80211_regdomain = NULL; 	if (!full_reset)		return; 	if (last_request != &core_request_world)		kfree(last_request);	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){	BUG_ON(!last_request); 	reset_regdomains(false); 	cfg80211_world_regdom = rd;	cfg80211_regdomain = rd;} bool is_world_regdom(const char *alpha2){	if (!alpha2)		return false;	if (alpha2[0] == '0' && alpha2[1] == '0')		return true;	return false;} static bool is_alpha2_set(const char *alpha2){	if (!alpha2)		return false;	if (alpha2[0] != 0 && alpha2[1] != 0)		return true;	return false;} 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	 */	if (alpha2[0] == '9' && alpha2[1] == '9')		return true;	return false;} 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	 */	if (alpha2[0] == '9' && alpha2[1] == '8')		return true;	return false;} static bool is_an_alpha2(const char *alpha2){	if (!alpha2)		return false;	if (isalpha(alpha2[0]) && isalpha(alpha2[1]))		return true;	return false;} static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y){	if (!alpha2_x || !alpha2_y)		return false;	if (alpha2_x[0] == alpha2_y[0] &&		alpha2_x[1] == alpha2_y[1])		return true;	return false;} static bool regdom_changes(const char *alpha2){	assert_cfg80211_lock(); 	if (!cfg80211_regdomain)		return true;	if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2))		return false;	return true;} /* * 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 int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,			 const struct ieee80211_regdomain *src_regd){	struct ieee80211_regdomain *regd;	int size_of_regd = 0;	unsigned int i; 	size_of_regd = sizeof(struct ieee80211_regdomain) +	  ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); 	regd = kzalloc(size_of_regd, GFP_KERNEL);	if (!regd)		return -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)); 	*dst_regd = regd;	return 0;} #ifdef CONFIG_CFG80211_INTERNAL_REGDBstruct reg_regdb_search_request {	char alpha2[2];	struct list_head list;}; static LIST_HEAD(reg_regdb_search_list);static DEFINE_MUTEX(reg_regdb_search_mutex); static void reg_regdb_search(struct work_struct *work){	struct reg_regdb_search_request *request;	const struct ieee80211_regdomain *curdom, *regdom;	int i, r; 	mutex_lock(&reg_regdb_search_mutex);	while (!list_empty(&reg_regdb_search_list)) {		request = list_first_entry(&reg_regdb_search_list,					   struct reg_regdb_search_request,					   list);		list_del(&request->list); 		for (i=0; i<reg_regdb_size; i++) {			curdom = reg_regdb[i]; 			if (!memcmp(request->alpha2, curdom->alpha2, 2)) {				r = reg_copy_regd(&regdom, curdom);				if (r)					break;				mutex_lock(&cfg80211_mutex);				set_regdom(regdom);				mutex_unlock(&cfg80211_mutex);				break;			}		} 		kfree(request);	}	mutex_unlock(&reg_regdb_search_mutex);} static DECLARE_WORK(reg_regdb_work, reg_regdb_search); static void reg_regdb_query(const char *alpha2){	struct reg_regdb_search_request *request; 	if (!alpha2)		return; 	request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL);	if (!request)		return; 	memcpy(request->alpha2, alpha2, 2); 	mutex_lock(&reg_regdb_search_mutex);	list_add_tail(&request->list, &reg_regdb_search_list);	mutex_unlock(&reg_regdb_search_mutex); 	schedule_work(&reg_regdb_work);} /* Feel free to add any other sanity checks here */static void reg_regdb_size_check(void){	/* We should ideally BUILD_BUG_ON() but then random builds would fail */	WARN_ONCE(!reg_regdb_size, "db.txt is empty, you should update it...");}#elsestatic inline void reg_regdb_size_check(void) {}static inline void reg_regdb_query(const char *alpha2) {}#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ /* * This lets us keep regulatory code which is updated on a regulatory * basis in userspace. Country information is filled in by * reg_device_uevent */static int call_crda(const char *alpha2){	if (!is_world_regdom((char *) alpha2))		pr_info("Calling CRDA for country: %c%c\n",			alpha2[0], alpha2[1]);	else		pr_info("Calling CRDA to update world regulatory domain\n"); 	/* query internal regulatory database (if it exists) */	reg_regdb_query(alpha2); 	return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);} /* Used by nl80211 before kmalloc'ing our regulatory domain */bool reg_is_valid_request(const char *alpha2){	assert_cfg80211_lock(); 	if (!last_request)		return false; 	return alpha2_equal(last_request->alpha2, alpha2);} /* Sanity check on a regulatory rule */static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule){	const struct ieee80211_freq_range *freq_range = &rule->freq_range;	u32 freq_diff; 	if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)		return false; 	if (freq_range->start_freq_khz > freq_range->end_freq_khz)		return false; 	freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; 	if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||			freq_range->max_bandwidth_khz > freq_diff)		return false; 	return true;} static bool is_valid_rd(const struct ieee80211_regdomain *rd){	const struct ieee80211_reg_rule *reg_rule = NULL;	unsigned int i; 	if (!rd->n_reg_rules)		return false; 	if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))		return false; 	for (i = 0; i < rd->n_reg_rules; i++) {		reg_rule = &rd->reg_rules[i];		if (!is_valid_reg_rule(reg_rule))			return false;	} 	return true;} static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,			    u32 center_freq_khz,			    u32 bw_khz){	u32 start_freq_khz, end_freq_khz; 	start_freq_khz = center_freq_khz - (bw_khz/2);	end_freq_khz = center_freq_khz + (bw_khz/2); 	if (start_freq_khz >= freq_range->start_freq_khz &&	    end_freq_khz <= freq_range->end_freq_khz)		return true; 	return false;} /** * freq_in_rule_band - tells us if a frequency is in a frequency band * @freq_range: frequency rule we want to query * @freq_khz: frequency we are inquiring about * * This lets us know if a specific frequency rule is or is not relevant to * a specific frequency's band. Bands are device specific and artificial * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is * safe for now to assume that a frequency rule should not be part of a * frequency's band if the start freq or end freq are off by more than 2 GHz. * This resolution can be lowered and should be considered as we add

Lines 1–500 of 2416.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t