Release 4.11 net/wireless/reg.c
/*
* 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
*
* 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/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 "rdev-ops.h"
#include "regdb.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 */
static struct platform_device *reg_pdev;
/*
* 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 spinlock_t 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);
static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
return rtnl_dereference(cfg80211_regdomain);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 17 | 100.00% | 2 | 100.00% |
Total | 17 | 100.00% | 2 | 100.00% |
const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
{
return rtnl_dereference(wiphy->regd);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 21 | 100.00% | 2 | 100.00% |
Total | 21 | 100.00% | 2 | 100.00% |
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";
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
{
const struct ieee80211_regdomain *regd = NULL;
const struct ieee80211_regdomain *wiphy_regd = NULL;
regd = get_cfg80211_regdom();
if (!wiphy)
goto out;
wiphy_regd = get_wiphy_regdom(wiphy);
if (!wiphy_regd)
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:
return regd->dfs_region;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 100 | 98.04% | 1 | 50.00% |
Johannes Berg | 2 | 1.96% | 1 | 50.00% |
Total | 102 | 100.00% | 2 | 100.00% |
static void rcu_free_regdom(const struct ieee80211_regdomain *r)
{
if (!r)
return;
kfree_rcu((struct ieee80211_regdomain *)r, rcu_head);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
static struct regulatory_request *get_last_request(void)
{
return rcu_dereference_rtnl(last_request);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 16 | 100.00% | 2 | 100.00% |
Total | 16 | 100.00% | 2 | 100.00% |
/* 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_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];
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 16 | 51.61% | 1 | 25.00% |
Johannes Berg | 8 | 25.81% | 1 | 25.00% |
Luis R. Rodriguez | 7 | 22.58% | 2 | 50.00% |
Total | 31 | 100.00% | 4 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 19 | 59.38% | 2 | 66.67% |
Arik Nemtsov | 13 | 40.62% | 1 | 33.33% |
Total | 32 | 100.00% | 3 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 36 | 94.74% | 2 | 66.67% |
Arik Nemtsov | 2 | 5.26% | 1 | 33.33% |
Total | 38 | 100.00% | 3 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 84 | 87.50% | 7 | 77.78% |
Luis R. Rodriguez | 12 | 12.50% | 2 | 22.22% |
Total | 96 | 100.00% | 9 | 100.00% |
/*
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 34 | 87.18% | 7 | 77.78% |
Luis R. Rodriguez | 5 | 12.82% | 2 | 22.22% |
Total | 39 | 100.00% | 9 | 100.00% |
bool is_world_regdom(const char *alpha2)
{
if (!alpha2)
return false;
return alpha2[0] == '0' && alpha2[1] == '0';
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 29 | 87.88% | 1 | 25.00% |
Johannes Berg | 4 | 12.12% | 3 | 75.00% |
Total | 33 | 100.00% | 4 | 100.00% |
static bool is_alpha2_set(const char *alpha2)
{
if (!alpha2)
return false;
return alpha2[0] && alpha2[1];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 27 | 90.00% | 1 | 25.00% |
Johannes Berg | 3 | 10.00% | 3 | 75.00% |
Total | 30 | 100.00% | 4 | 100.00% |
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';
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 31 | 88.57% | 2 | 40.00% |
Johannes Berg | 4 | 11.43% | 3 | 60.00% |
Total | 35 | 100.00% | 5 | 100.00% |
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';
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 34 | 97.14% | 2 | 66.67% |
Johannes Berg | 1 | 2.86% | 1 | 33.33% |
Total | 35 | 100.00% | 3 | 100.00% |
static bool is_an_alpha2(const char *alpha2)
{
if (!alpha2)
return false;
return isalpha(alpha2[0]) && isalpha(alpha2[1]);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 25 | 69.44% | 1 | 16.67% |
Johannes Berg | 8 | 22.22% | 3 | 50.00% |
John W. Linville | 2 | 5.56% | 1 | 16.67% |
Tomas Winkler | 1 | 2.78% | 1 | 16.67% |
Total | 36 | 100.00% | 6 | 100.00% |
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];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 43 | 89.58% | 1 | 25.00% |
Johannes Berg | 3 | 6.25% | 2 | 50.00% |
Tomas Winkler | 2 | 4.17% | 1 | 25.00% |
Total | 48 | 100.00% | 4 | 100.00% |
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);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 13 | 33.33% | 3 | 42.86% |
Luis R. Rodriguez | 13 | 33.33% | 3 | 42.86% |
John W. Linville | 13 | 33.33% | 1 | 14.29% |
Total | 39 | 100.00% | 7 | 100.00% |
/*
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 63 | 100.00% | 1 | 100.00% |
Total | 63 | 100.00% | 1 | 100.00% |
static const struct ieee80211_regdomain *
reg_copy_regd(const struct ieee80211_regdomain *src_regd)
{
struct ieee80211_regdomain *regd;
int size_of_regd;
unsigned int i;
size_of_regd =
sizeof(struct ieee80211_regdomain) +
src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
regd = kzalloc(size_of_regd, GFP_KERNEL);
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(®d->reg_rules[i], &src_regd->reg_rules[i],
sizeof(struct ieee80211_reg_rule));
return regd;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
John W. Linville | 116 | 95.08% | 1 | 50.00% |
Johannes Berg | 6 | 4.92% | 1 | 50.00% |
Total | 122 | 100.00% | 2 | 100.00% |
#ifdef CONFIG_CFG80211_INTERNAL_REGDB
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(®_regdb_apply_mutex);
while (!list_empty(®_regdb_apply_list)) {
request = list_first_entry(®_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(®_regdb_apply_mutex);
rtnl_unlock();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
John W. Linville | 61 | 76.25% | 2 | 33.33% |
Johannes Berg | 15 | 18.75% | 3 | 50.00% |
Luis R. Rodriguez | 4 | 5.00% | 1 | 16.67% |
Total | 80 | 100.00% | 6 | 100.00% |
static DECLARE_WORK(reg_regdb_work, reg_regdb_apply);
static int reg_query_builtin(const char *alpha2)
{
const struct ieee80211_regdomain *regdom = NULL;
struct reg_regdb_apply_request *request;
unsigned int i;
for (i = 0; i < reg_regdb_size; i++) {
if (alpha2_equal(alpha2, reg_regdb[i]->alpha2)) {
regdom = reg_regdb[i];
break;
}
}
if (!regdom)
return -ENODATA;
request = kzalloc(sizeof(struct reg_regdb_apply_request), GFP_KERNEL);
if (!request)
return -ENOMEM;
request->regdom = reg_copy_regd(regdom);
if (IS_ERR_OR_NULL(request->regdom)) {
kfree(request);
return -ENOMEM;
}
mutex_lock(®_regdb_apply_mutex);
list_add_tail(&request->list, ®_regdb_apply_list);
mutex_unlock(®_regdb_apply_mutex);
schedule_work(®_regdb_work);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 98 | 61.64% | 2 | 33.33% |
John W. Linville | 50 | 31.45% | 2 | 33.33% |
Luis R. Rodriguez | 11 | 6.92% | 2 | 33.33% |
Total | 159 | 100.00% | 6 | 100.00% |
/* 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...");
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 17 | 100.00% | 1 | 100.00% |
Total | 17 | 100.00% | 1 | 100.00% |
#else
static inline void reg_regdb_size_check(void) {}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 8 | 100.00% | 1 | 100.00% |
Total | 8 | 100.00% | 1 | 100.00% |
static inline int reg_query_builtin(const char *alpha2)
{
return -ENODATA;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
John W. Linville | 8 | 50.00% | 1 | 33.33% |
Johannes Berg | 8 | 50.00% | 2 | 66.67% |
Total | 16 | 100.00% | 3 | 100.00% |
#endif /* CONFIG_CFG80211_INTERNAL_REGDB */
#ifdef CONFIG_CFG80211_CRDA_SUPPORT
/* Max number of consecutive attempts to communicate with CRDA */
#define REG_MAX_CRDA_TIMEOUTS 10
static u32 reg_crda_timeouts;
static void crda_timeout_work(struct work_struct *work);
static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work);
static void crda_timeout_work(struct work_struct *work)
{
pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
rtnl_lock();
reg_crda_timeouts++;
restore_regulatory_settings(true);
rtnl_unlock();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 30 | 100.00% | 2 | 100.00% |
Total | 30 | 100.00% | 2 | 100.00% |
static void cancel_crda_timeout(void)
{
cancel_delayed_work(&crda_timeout);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 14 | 100.00% | 1 | 100.00% |
Total | 14 | 100.00% | 1 | 100.00% |
static void cancel_crda_timeout_sync(void)
{
cancel_delayed_work_sync(&crda_timeout);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 14 | 100.00% | 1 | 100.00% |
Total | 14 | 100.00% | 1 | 100.00% |
static void reset_crda_timeouts(void)
{
reg_crda_timeouts = 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 12 | 100.00% | 1 | 100.00% |
Total | 12 | 100.00% | 1 | 100.00% |
/*
* This lets us keep regulatory code which is updated on a regulatory
* basis in userspace.
*/
static int call_crda(const char *alpha2)
{
char country[12];
char *env[] = { country, NULL };
int ret;
snprintf(country, sizeof(country), "COUNTRY=%c%c",
alpha2[0], alpha2[1]);
if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) {
pr_debug("Exceeded CRDA call max attempts. Not calling CRDA\n");
return -EINVAL;
}
if (!is_world_regdom((char *) alpha2))
pr_debug("Calling CRDA for country: %c%c\n",
alpha2[0], alpha2[1]);
else
pr_debug("Calling CRDA to update world regulatory domain\n");
ret = kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env);
if (ret)
return ret;
queue_delayed_work(system_power_efficient_wq,
&crda_timeout, msecs_to_jiffies(3142));
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 70 | 49.30% | 3 | 42.86% |
Luis R. Rodriguez | 51 | 35.92% | 1 | 14.29% |
Ilan Peer | 16 | 11.27% | 1 | 14.29% |
Thomas Petazzoni | 3 | 2.11% | 1 | 14.29% |
Joe Perches | 2 | 1.41% | 1 | 14.29% |
Total | 142 | 100.00% | 7 | 100.00% |
#else
static inline void cancel_crda_timeout(void) {}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 8 | 100.00% | 1 | 100.00% |
Total | 8 | 100.00% | 1 | 100.00% |
static inline void cancel_crda_timeout_sync(void) {}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 8 | 100.00% | 1 | 100.00% |
Total | 8 | 100.00% | 1 | 100.00% |
static inline void reset_crda_timeouts(void) {}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 8 | 100.00% | 1 | 100.00% |
Total | 8 | 100.00% | 1 | 100.00% |
static inline int call_crda(const char *alpha2)
{
return -ENODATA;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 16 | 100.00% | 1 | 100.00% |
Total | 16 | 100.00% | 1 | 100.00% |
#endif /* CONFIG_CFG80211_CRDA_SUPPORT */
static bool reg_query_database(struct regulatory_request *request)
{
/* query internal regulatory database (if it exists) */
if (reg_query_builtin(request->alpha2) == 0)
return true;
if (call_crda(request->alpha2) == 0)
return true;
return false;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 21 | 48.84% | 4 | 66.67% |
Luis R. Rodriguez | 20 | 46.51% | 1 | 16.67% |
Ilan Peer | 2 | 4.65% | 1 | 16.67% |
Total | 43 | 100.00% | 6 | 100.00% |
bool reg_is_valid_request(const char *alpha2)
{
struct regulatory_request *lr = get_last_request();
if (!lr || lr->processed)
return false;
return alpha2_equal(lr->alpha2, alpha2);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 23 | 57.50% | 2 | 33.33% |
Johannes Berg | 17 | 42.50% | 4 | 66.67% |
Total | 40 | 100.00% | 6 | 100.00% |
static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy)
{
struct regulatory_request *lr = get_last_request();
/*
* Follow the driver's regulatory domain, if present, unless a country
* IE has been processed or a user wants to help complaince further
*/
if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
lr->initiator != NL80211_REGDOM_SET_BY_USER &&
wiphy->regd)
return get_wiphy_regdom(wiphy);
return get_cfg80211_regdom();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Janusz Dziedzic | 51 | 100.00% | 1 | 100.00% |
Total | 51 | 100.00% | 1 | 100.00% |
static unsigned int
reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd,
const struct ieee80211_reg_rule *rule)
{
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
const struct ieee80211_freq_range *freq_range_tmp;
const struct ieee80211_reg_rule *tmp;
u32 start_freq, end_freq, idx, no;
for (idx = 0; idx < rd->n_reg_rules; idx++)
if (rule == &rd->reg_rules[idx])
break;
if (idx == rd->n_reg_rules)
return 0;
/* get start_freq */
no = idx;
while (no) {
tmp = &rd->reg_rules[--no];
freq_range_tmp = &tmp->freq_range;
if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz)
break;
freq_range = freq_range_tmp;
}
start_freq = freq_range->start_freq_khz;
/* get end_freq */
freq_range = &rule->freq_range;
no = idx;
while (no < rd->n_reg_rules - 1) {
tmp = &rd->reg_rules[++no];
freq_range_tmp = &tmp->freq_range;
if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz)
break;
freq_range = freq_range_tmp;
}
end_freq = freq_range->end_freq_khz;
return end_freq - start_freq;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Janusz Dziedzic | 206 | 99.04% | 1 | 50.00% |
Arik Nemtsov | 2 | 0.96% | 1 | 50.00% |
Total | 208 | 100.00% | 2 | 100.00% |
unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
const struct ieee80211_reg_rule *rule)
{
unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule);
if (rule->flags & NL80211_RRF_NO_160MHZ)
bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80));
if (rule->flags & NL80211_RRF_NO_80MHZ)
bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40));
/*
* HT40+/HT40- limits are handled per-channel. Only limit BW if both
* are not allowed.
*/
if (rule->flags & NL80211_RRF_NO_HT40MINUS &&
rule->flags & NL80211_RRF_NO_HT40PLUS)
bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20));
return bw;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Arik Nemtsov | 102 | 100.00% | 1 | 100.00% |
Total | 102 | 100.00% | 1 | 100.00% |
/* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 80 | 90.91% | 3 | 60.00% |
Roel Kluin | 6 | 6.82% | 1 | 20.00% |
Johannes Berg | 2 | 2.27% | 1 | 20.00% |
Total | 88 | 100.00% | 5 | 100.00% |
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 87 | 97.75% | 2 | 66.67% |
Johannes Berg | 2 | 2.25% | 1 | 33.33% |
Total | 89 | 100.00% | 3 | 100.00% |
/**
* 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", the "5 GHz band" and the "60GHz 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 for the 2.4 and 5 GHz bands, and by more than 10 GHz for the
* 60 GHz band.
* This resolution can be lowered and should be considered as we add
* regulatory rule support for other "bands".
**/
static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
u32 freq_khz)
{
#define ONE_GHZ_IN_KHZ 1000000
/*
* From 802.11ad: directional multi-gigabit (DMG):
* Pertaining to operation in a frequency band containing a channel
* with the Channel starting frequency above 45 GHz.
*/
u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ?
10 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ;
if (abs(freq_khz - freq_range->start_freq_khz) <= limit)
return true;
if (abs(freq_khz - freq_range->end_freq_khz) <= limit)
return true;
return false;
#undef ONE_GHZ_IN_KHZ
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
John W. Linville | 45 | 60.00% | 1 | 20.00% |
Vladimir Kondratiev | 20 | 26.67% | 1 | 20.00% |
Luis R. Rodriguez | 10 | 13.33% | 3 | 60.00% |
Total | 75 | 100.00% | 5 | 100.00% |
/*
* Later on we can perhaps use the more restrictive DFS
* region but we don't have information for that yet so
* for now simply disallow conflicts.
*/
static enum nl80211_dfs_regions
reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
const enum nl80211_dfs_regions dfs_region2)
{
if (dfs_region1 != dfs_region2)
return NL80211_DFS_UNSET;
return dfs_region1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Luis R. Rodriguez | 29 | 100.00% | 1 | 100.00% |
Total | 29 | 100.00% | 1 | 100.00% |
/*
* Helper for regdom_intersect(), this does the real
* mathematical intersection fun
*/
static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
const struct ieee80211_regdomain *rd2,
const struct ieee80211_reg_rule *rule1,
const struct ieee80211_reg_rule *rule2,
struct ieee80211_reg_rule *intersected_rule)
{
const struct ieee80211_freq_range *freq_range1, *freq_range2;
struct ieee80211_freq_range *freq_range;
const struct ieee80211_power_rule *power_rule1, *power_rule2;
struct ieee80211_power_rule *power_rule;
u32 freq_diff, max_bandwidth1, max_bandwidth2;
freq_range1 = &rule1->freq_range;
freq_range2 = &rule2->freq_range;
freq_range = &intersected_rule->freq_range;
power_rule1 = &rule1->power_rule;
power_rule2 = &rule2->power_rule;
power_rule = &intersected_rule->power_rule;
freq_range->start_freq_khz = max(