/* * 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(®_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(®d->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(®_regdb_search_mutex); while (!list_empty(®_regdb_search_list)) { request = list_first_entry(®_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(®dom, curdom); if (r) break; mutex_lock(&cfg80211_mutex); set_regdom(regdom); mutex_unlock(&cfg80211_mutex); break; } } kfree(request); } mutex_unlock(®_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(®_regdb_search_mutex); list_add_tail(&request->list, ®_regdb_search_list); mutex_unlock(®_regdb_search_mutex); schedule_work(®_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(®_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 addLines 1–500 of 2416. Load more
/* * 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(®_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(®d->reg_rules[i], &src_regd->reg_rules[i], sizeof(struct ieee80211_reg_rule)); *dst_regd = regd; return 0; } #ifdef CONFIG_CFG80211_INTERNAL_REGDB struct 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(®_regdb_search_mutex); while (!list_empty(®_regdb_search_list)) { request = list_first_entry(®_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(®dom, curdom); if (r) break; mutex_lock(&cfg80211_mutex); set_regdom(regdom); mutex_unlock(&cfg80211_mutex); break; } } kfree(request); } mutex_unlock(®_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(®_regdb_search_mutex); list_add_tail(&request->list, ®_regdb_search_list); mutex_unlock(®_regdb_search_mutex); schedule_work(®_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..."); } #else static 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(®_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