wireless: support internal statically compiled regulatory database
This patch provides infrastructure for machine translation of the regulatory rules database used by CRDA into a C data structure. It includes code for searching that database as an alternative to dynamic regulatory rules updates via CRDA. Most people should use CRDA instead of this infrastructure, but it provides a better alternative than the WIRELESS_OLD_REGULATORY infrastructure (which can now be removed). Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Родитель
59d9cb071d
Коммит
3b377ea9d4
|
@ -188,3 +188,27 @@ Then in some part of your code after your wiphy has been registered:
|
|||
&mydriver_jp_regdom.reg_rules[i],
|
||||
sizeof(struct ieee80211_reg_rule));
|
||||
regulatory_struct_hint(rd);
|
||||
|
||||
Statically compiled regulatory database
|
||||
---------------------------------------
|
||||
|
||||
In most situations the userland solution using CRDA as described
|
||||
above is the preferred solution. However in some cases a set of
|
||||
rules built into the kernel itself may be desirable. To account
|
||||
for this situation, a configuration option has been provided
|
||||
(i.e. CONFIG_CFG80211_INTERNAL_REGDB). With this option enabled,
|
||||
the wireless database information contained in net/wireless/db.txt is
|
||||
used to generate a data structure encoded in net/wireless/regdb.c.
|
||||
That option also enables code in net/wireless/reg.c which queries
|
||||
the data in regdb.c as an alternative to using CRDA.
|
||||
|
||||
The file net/wireless/db.txt should be kept up-to-date with the db.txt
|
||||
file available in the git repository here:
|
||||
|
||||
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
|
||||
|
||||
Again, most users in most situations should be using the CRDA package
|
||||
provided with their distribution, and in most other situations users
|
||||
should be building and using CRDA on their own rather than using
|
||||
this option. If you are not absolutely sure that you should be using
|
||||
CONFIG_CFG80211_INTERNAL_REGDB then _DO_NOT_USE_IT_.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
regdb.c
|
|
@ -109,6 +109,22 @@ config WIRELESS_OLD_REGULATORY
|
|||
|
||||
Say N and if you say Y, please tell us why. The default is N.
|
||||
|
||||
config CFG80211_INTERNAL_REGDB
|
||||
bool "use statically compiled regulatory rules database" if EMBEDDED
|
||||
default n
|
||||
depends on CFG80211
|
||||
---help---
|
||||
This option generates an internal data structure representing
|
||||
the wireless regulatory rules described in net/wireless/db.txt
|
||||
and includes code to query that database. This is an alternative
|
||||
to using CRDA for defining regulatory rules for the kernel.
|
||||
|
||||
For details see:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
||||
Most distributions have a CRDA package. So if unsure, say N.
|
||||
|
||||
config CFG80211_WEXT
|
||||
bool "cfg80211 wireless extensions compatibility"
|
||||
depends on CFG80211
|
||||
|
|
|
@ -13,5 +13,11 @@ cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
|
|||
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o
|
||||
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
|
||||
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
|
||||
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__
|
||||
|
||||
$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk
|
||||
@$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@
|
||||
|
||||
clean-files := regdb.c
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# This file is a placeholder to prevent accidental build breakage if someone
|
||||
# enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to
|
||||
# enable that build option.
|
||||
#
|
||||
# You should be using CRDA instead. It is even better if you use the CRDA
|
||||
# package provided by your distribution, since they will probably keep it
|
||||
# up-to-date on your behalf.
|
||||
#
|
||||
# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
|
||||
# need to replace this file with one containing appropriately formatted
|
||||
# regulatory rules that cover the regulatory domains you will be using. Your
|
||||
# best option is to extract the db.txt file from the wireless-regdb git
|
||||
# repository:
|
||||
#
|
||||
# git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
|
||||
#
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/awk -f
|
||||
#
|
||||
# genregdb.awk -- generate regdb.c from db.txt
|
||||
#
|
||||
# Actually, it reads from stdin (presumed to be db.txt) and writes
|
||||
# to stdout (presumed to be regdb.c), but close enough...
|
||||
#
|
||||
# Copyright 2009 John W. Linville <linville@tuxdriver.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
|
||||
BEGIN {
|
||||
active = 0
|
||||
rules = 0;
|
||||
print "/*"
|
||||
print " * DO NOT EDIT -- file generated from data in db.txt"
|
||||
print " */"
|
||||
print ""
|
||||
print "#include <linux/nl80211.h>"
|
||||
print "#include <net/cfg80211.h>"
|
||||
print ""
|
||||
regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
|
||||
}
|
||||
|
||||
/^[ \t]*#/ {
|
||||
/* Ignore */
|
||||
}
|
||||
|
||||
!active && /^[ \t]*$/ {
|
||||
/* Ignore */
|
||||
}
|
||||
|
||||
!active && /country/ {
|
||||
country=$2
|
||||
sub(/:/, "", country)
|
||||
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
|
||||
printf "\t.alpha2 = \"%s\",\n", country
|
||||
printf "\t.reg_rules = {\n"
|
||||
active = 1
|
||||
regdb = regdb "\t®dom_" country ",\n"
|
||||
}
|
||||
|
||||
active && /^[ \t]*\(/ {
|
||||
start = $1
|
||||
sub(/\(/, "", start)
|
||||
end = $3
|
||||
bw = $5
|
||||
sub(/\),/, "", bw)
|
||||
gain = $6
|
||||
sub(/\(/, "", gain)
|
||||
sub(/,/, "", gain)
|
||||
power = $7
|
||||
sub(/\)/, "", power)
|
||||
sub(/,/, "", power)
|
||||
# power might be in mW...
|
||||
units = $8
|
||||
sub(/\)/, "", units)
|
||||
sub(/,/, "", units)
|
||||
if (units == "mW") {
|
||||
if (power == 100) {
|
||||
power = 20
|
||||
} else if (power == 200) {
|
||||
power = 23
|
||||
} else if (power == 500) {
|
||||
power = 27
|
||||
} else if (power == 1000) {
|
||||
power = 30
|
||||
} else {
|
||||
print "Unknown power value in database!"
|
||||
}
|
||||
}
|
||||
flagstr = ""
|
||||
for (i=8; i<=NF; i++)
|
||||
flagstr = flagstr $i
|
||||
split(flagstr, flagarray, ",")
|
||||
flags = ""
|
||||
for (arg in flagarray) {
|
||||
if (flagarray[arg] == "NO-OFDM") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | "
|
||||
} else if (flagarray[arg] == "NO-CCK") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | "
|
||||
} else if (flagarray[arg] == "NO-INDOOR") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | "
|
||||
} else if (flagarray[arg] == "NO-OUTDOOR") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | "
|
||||
} else if (flagarray[arg] == "DFS") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_DFS | "
|
||||
} else if (flagarray[arg] == "PTP-ONLY") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | "
|
||||
} else if (flagarray[arg] == "PTMP-ONLY") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
|
||||
} else if (flagarray[arg] == "PASSIVE-SCAN") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
|
||||
} else if (flagarray[arg] == "NO-IBSS") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
|
||||
}
|
||||
}
|
||||
flags = flags "0"
|
||||
printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
|
||||
rules++
|
||||
}
|
||||
|
||||
active && /^[ \t]*$/ {
|
||||
active = 0
|
||||
printf "\t},\n"
|
||||
printf "\t.n_reg_rules = %d\n", rules
|
||||
printf "};\n\n"
|
||||
rules = 0;
|
||||
}
|
||||
|
||||
END {
|
||||
print regdb "};"
|
||||
print ""
|
||||
print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
|
||||
}
|
|
@ -40,6 +40,7 @@
|
|||
#include <net/cfg80211.h>
|
||||
#include "core.h"
|
||||
#include "reg.h"
|
||||
#include "regdb.h"
|
||||
#include "nl80211.h"
|
||||
|
||||
/* Receipt of information from last regulatory request */
|
||||
|
@ -360,6 +361,98 @@ static bool country_ie_integrity_changes(u32 checksum)
|
|||
return false;
|
||||
}
|
||||
|
||||
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_SPINLOCK(reg_regdb_search_lock);
|
||||
|
||||
static void reg_regdb_search(struct work_struct *work)
|
||||
{
|
||||
struct reg_regdb_search_request *request;
|
||||
const struct ieee80211_regdomain *curdom, *regdom;
|
||||
int i, r;
|
||||
|
||||
spin_lock(®_regdb_search_lock);
|
||||
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;
|
||||
spin_unlock(®_regdb_search_lock);
|
||||
mutex_lock(&cfg80211_mutex);
|
||||
set_regdom(regdom);
|
||||
mutex_unlock(&cfg80211_mutex);
|
||||
spin_lock(®_regdb_search_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(request);
|
||||
}
|
||||
spin_unlock(®_regdb_search_lock);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
spin_lock(®_regdb_search_lock);
|
||||
list_add_tail(&request->list, ®_regdb_search_list);
|
||||
spin_unlock(®_regdb_search_lock);
|
||||
|
||||
schedule_work(®_regdb_work);
|
||||
}
|
||||
#else
|
||||
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.
|
||||
|
@ -379,6 +472,9 @@ static int call_crda(const char *alpha2)
|
|||
printk(KERN_INFO "cfg80211: Calling CRDA to update world "
|
||||
"regulatory domain\n");
|
||||
|
||||
/* query internal regulatory database (if it exists) */
|
||||
reg_regdb_query(alpha2);
|
||||
|
||||
country_env[8] = alpha2[0];
|
||||
country_env[9] = alpha2[1];
|
||||
|
||||
|
@ -1367,30 +1463,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
|
|||
}
|
||||
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return value which can be used by ignore_request() to indicate
|
||||
* it has been determined we should intersect two regulatory domains
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef __REGDB_H__
|
||||
#define __REGDB_H__
|
||||
|
||||
extern const struct ieee80211_regdomain *reg_regdb[];
|
||||
extern int reg_regdb_size;
|
||||
|
||||
#endif /* __REGDB_H__ */
|
Загрузка…
Ссылка в новой задаче